diff -pruN 1:0.10.28-3/.travis.yml 1:0.10.29-1/.travis.yml
--- 1:0.10.28-3/.travis.yml	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/.travis.yml	2025-12-07 15:22:42.000000000 +0000
@@ -29,6 +29,10 @@ addons:
       - libunwind-dev
       - wget
       - libbrotli1
+      - lsb-release
+      - wget
+      - gnupg
+      - ca-certificates
 
 cache:
   directories:
@@ -42,13 +46,12 @@ env:
     - LUAJIT_LIB=$LUAJIT_PREFIX/lib
     - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
     - LUA_INCLUDE_DIR=$LUAJIT_INC
-    - PCRE_PREFIX=/opt/pcre
+    #- PCRE2_PREFIX=/usr/local/openresty/pcre2
     - PCRE2_PREFIX=/opt/pcre2
-    - PCRE_LIB=$PCRE_PREFIX/lib
     - PCRE2_LIB=$PCRE2_PREFIX/lib
-    - PCRE_INC=$PCRE_PREFIX/include
     - PCRE2_INC=$PCRE2_PREFIX/include
-    - OPENSSL_PREFIX=/opt/ssl
+    #- OPENSSL_PREFIX=/usr/local/openresty/openssl3
+    - OPENSSL_PREFIX=/opt/openssl3
     - OPENSSL_LIB=$OPENSSL_PREFIX/lib
     - OPENSSL_INC=$OPENSSL_PREFIX/include
     - LIBDRIZZLE_PREFIX=/opt/drizzle
@@ -59,14 +62,10 @@ env:
     - TEST_NGINX_SLEEP=0.006
     - MALLOC_PERTURB_=9
   jobs:
-    #- NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.1w OPENSSL_PATCH_VER=1.1.1f
-    #- NGINX_VERSION=1.25.1 OPENSSL_VER=1.1.1w TEST_NGINX_USE_HTTP2=1
-    - NGINX_VERSION=1.27.1 OPENSSL_VER=1.1.1w OPENSSL_PATCH_VER=1.1.1f TEST_NGINX_TIMEOUT=5 PCRE_VER=8.45
-    - NGINX_VERSION=1.27.1 OPENSSL_VER=3.0.15 OPENSSL_PATCH_VER=3.0.15 TEST_NGINX_TIMEOUT=5 PCRE2_VER=10.42
-    - NGINX_VERSION=1.27.1 OPENSSL_VER=1.1.1w OPENSSL_PATCH_VER=1.1.1f TEST_NGINX_TIMEOUT=5 PCRE_VER=8.45 TEST_NGINX_USE_HTTP2=1
-    - NGINX_VERSION=1.27.1 OPENSSL_VER=3.0.15 OPENSSL_PATCH_VER=3.0.15 TEST_NGINX_TIMEOUT=5 PCRE2_VER=10.42 TEST_NGINX_USE_HTTP2=1
-    - NGINX_VERSION=1.27.1 OPENSSL_VER=3.0.15 OPENSSL_PATCH_VER=3.0.15 TEST_NGINX_USE_HTTP3=1 TEST_NGINX_QUIC_IDLE_TIMEOUT=3 PCRE2_VER=10.42
-    - NGINX_VERSION=1.27.1 BORINGSSL=1 TEST_NGINX_USE_HTTP3=1 TEST_NGINX_QUIC_IDLE_TIMEOUT=3 PCRE2_VER=10.42
+    - NGINX_VERSION=1.29.2 OPENSSL_VER=3.5.4 OPENSSL_PATCH_VER=3.5.4 TEST_NGINX_TIMEOUT=5 PCRE2_VER=10.46
+    - NGINX_VERSION=1.29.2 OPENSSL_VER=3.5.4 OPENSSL_PATCH_VER=3.5.4 TEST_NGINX_TIMEOUT=5 PCRE2_VER=10.46 TEST_NGINX_USE_HTTP2=1
+    - NGINX_VERSION=1.29.2 OPENSSL_VER=3.5.4 OPENSSL_PATCH_VER=3.5.4 TEST_NGINX_USE_HTTP3=1 TEST_NGINX_QUIC_IDLE_TIMEOUT=3 PCRE2_VER=10.46
+    - NGINX_VERSION=1.29.2 BORINGSSL=1 TEST_NGINX_USE_HTTP3=1 TEST_NGINX_QUIC_IDLE_TIMEOUT=3 PCRE2_VER=10.46
 
 services:
   - memcached
@@ -77,15 +76,16 @@ before_install:
   - '! grep -n -P ''(?<=.{80}).+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo "ERROR: Found C source lines exceeding 80 columns." > /dev/stderr; exit 1)'
   - '! grep -n -P ''\t+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo "ERROR: Cannot use tabs." > /dev/stderr; exit 1)'
   - /usr/bin/env perl $(command -v cpanm) --sudo --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)
+  - wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -
+  - echo "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list
+  - sudo apt-get update
+  - sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openresty-pcre2 openresty-openssl3 openresty-pcre2-dev openresty-openssl3-dev
+
 
 install:
   - if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then wget -P download-cache https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/drizzle7-$DRIZZLE_VER.tar.gz; fi
-  #- if [ -n "$PCRE_VER" ] && [ ! -f download-cache/pcre-$PCRE_VER.tar.gz ]; then wget -P download-cache https://downloads.sourceforge.net/project/pcre/pcre/${PCRE_VER}/pcre-${PCRE_VER}.tar.gz; fi
-  #- if [ -n "$PCRE2_VER" ] && [ ! -f download-cache/pcre2-$PCRE2_VER.tar.gz ]; then wget -P download-cache https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VER}/pcre2-${PCRE2_VER}.tar.gz; fi
-  #- if [ -n "$OPENSSL_VER" ] && [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://github.com/openssl/openssl/releases/download/openssl-$OPENSSL_VER/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi
-  - if [ -n "$OPENSSL_VER" ]; then wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v1.0.0/openssl-${OPENSSL_VER}-x64-focal.tar.gz; fi
-  - if [ -n "$PCRE_VER" ]; then wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v1.0.0/pcre-${PCRE_VER}-x64-focal.tar.gz; fi
-  - if [ -n "$PCRE2_VER" ]; then wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v1.0.0/pcre2-${PCRE2_VER}-x64-focal.tar.gz; fi
+  - if [ -n "$PCRE2_VER" ] && [ ! -f download-cache/pcre2-$PCRE2_VER.tar.gz ]; then wget -P download-cache https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VER}/pcre2-${PCRE2_VER}.tar.gz; fi
+  - if [ -n "$OPENSSL_VER" ] && [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://github.com/openssl/openssl/releases/download/openssl-$OPENSSL_VER/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi
   - wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/boringssl-20230902-x64-focal.tar.gz
   - wget https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/curl-h3-x64-focal.tar.gz
   - git clone https://github.com/openresty/test-nginx.git
@@ -137,13 +137,9 @@ script:
   - sudo make install-libdrizzle-1.0 > build.log 2>&1 || (cat build.log && exit 1)
   - cd ../mockeagain/ && make CC=$CC -j$JOBS && cd ..
   - cd lua-cjson/ && make -j$JOBS && sudo make install && cd ..
-  #- if [ -n "PCRE_VER" ]; then tar zxf download-cache/pcre-$PCRE_VER.tar.gz; cd pcre-$PCRE_VER/; ./configure --prefix=$PCRE_PREFIX --enable-jit --enable-utf --enable-unicode-properties > build.log 2>&1 || (cat build.log && exit 1); make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); sudo PATH=$PATH make install > build.log 2>&1 || (cat build.log && exit 1); cd ..; fi
-  #- if [ -n "$PCRE2_VER" ]; then tar zxf download-cache/pcre2-$PCRE2_VER.tar.gz; cd pcre2-$PCRE2_VER/; ./configure --prefix=$PCRE2_PREFIX --enable-jit --enable-utf > build.log 2>&1 || (cat build.log && exit 1); make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); sudo PATH=$PATH make install > build.log 2>&1 || (cat build.log && exit 1); cd ..; fi
-  #- if [ -n "$OPENSSL_VER" ]; then tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz; cd openssl-$OPENSSL_VER/; patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch; ./config shared enable-ssl3 enable-ssl3-method -g --prefix=$OPENSSL_PREFIX --libdir=lib -DPURIFY > build.log 2>&1 || (cat build.log && exit 1); make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1); cd ..; fi
-  - if [ -n "$BORINGSSL" ]; then sudo mkdir -p /opt/ssl && sudo tar -C /opt/ssl -xf boringssl-20230902-x64-focal.tar.gz --strip-components=1; fi
-  - if [ -n "$OPENSSL_VER" ]; then sudo mkdir -p /opt/ssl && sudo tar -C /opt/ssl -xf openssl-$OPENSSL_VER-x64-focal.tar.gz --strip-components=2; fi
-  - if [ -n "$PCRE_VER" ]; then sudo mkdir -p $PCRE_PREFIX && sudo tar -C $PCRE_PREFIX -xf pcre-$PCRE_VER-x64-focal.tar.gz --strip-components=2; fi
-  - if [ -n "$PCRE2_VER" ]; then sudo mkdir -p $PCRE2_PREFIX && sudo tar -C $PCRE2_PREFIX -xf pcre2-$PCRE2_VER-x64-focal.tar.gz --strip-components=2; fi
+  - if [ -n "$PCRE2_VER" ]; then tar zxf download-cache/pcre2-$PCRE2_VER.tar.gz; cd pcre2-$PCRE2_VER/; ./configure --prefix=$PCRE2_PREFIX --enable-jit --enable-utf > build.log 2>&1 || (cat build.log && exit 1); make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); sudo PATH=$PATH make install > build.log 2>&1 || (cat build.log && exit 1); cd ..; fi
+  - if [ -n "$OPENSSL_VER" ]; then tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz; cd openssl-$OPENSSL_VER/; patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch; ./config shared enable-ssl3 enable-ssl3-method -g --prefix=$OPENSSL_PREFIX --libdir=lib -DPURIFY > build.log 2>&1 || (cat build.log && exit 1); make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1); sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1); cd ..; fi
+  - if [ -n "$BORINGSSL" ]; then sudo rm -fr /usr/local/openresty/openssl3/ && sudo mkdir -p /usr/local/openresty/openssl3 && sudo tar -C /usr/local/openresty/openssl3 -xf boringssl-20230902-x64-focal.tar.gz --strip-components=1; fi
   - export NGX_BUILD_CC=$CC
   - sh util/build-without-ssl.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)
   - sh util/build-with-dd.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)
@@ -160,4 +156,4 @@ script:
   - dig +short myip.opendns.com @resolver1.opendns.com || exit 0
   - dig +short @$TEST_NGINX_RESOLVER openresty.org || exit 0
   - dig +short @$TEST_NGINX_RESOLVER agentzh.org || exit 0
-  - /usr/bin/env perl $(command -v prove) -I. -Itest-nginx/lib -r t/
+  - /usr/bin/env perl $(command -v prove) -I. -Itest-nginx/inc -Itest-nginx/lib -r t/
diff -pruN 1:0.10.28-3/README.markdown 1:0.10.29-1/README.markdown
--- 1:0.10.28-3/README.markdown	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/README.markdown	2025-12-07 15:22:42.000000000 +0000
@@ -45,6 +45,7 @@ Table of Contents
     * [Missing data on short circuited requests](#missing-data-on-short-circuited-requests)
 * [TODO](#todo)
 * [Changes](#changes)
+* [Build And Test](#build-and-test)
 * [Test Suite](#test-suite)
 * [Copyright and License](#copyright-and-license)
 * [See Also](#see-also)
@@ -63,8 +64,8 @@ Version
 =======
 
 This document describes ngx_lua
-[v0.10.25](https://github.com/openresty/lua-nginx-module/tags), which was released
-on 19 June 2023.
+[v0.10.28](https://github.com/openresty/lua-nginx-module/tags), which was released
+on 17 Jan, 2025.
 
 Videos
 ======
@@ -307,6 +308,8 @@ Nginx Compatibility
 
 The latest version of this module is compatible with the following versions of Nginx:
 
+* 1.29.x  (last tested: 1.29.2)
+* 1.27.x  (last tested: 1.27.1)
 * 1.25.x  (last tested: 1.25.1)
 * 1.21.x  (last tested: 1.21.4)
 * 1.19.x  (last tested: 1.19.3)
@@ -982,6 +985,23 @@ The changes made in every release of thi
 
 [Back to TOC](#table-of-contents)
 
+Build And Test
+==============
+
+This module uses `.travis.yml` as the CI configuration.
+You can always check `.travis.yml` for the latest CI configuration.
+
+For developers, you need to run tests locally. You can use `util/run-ci.sh`
+to easily set up the environment and execute the test suite.
+
+To run the Test from the beginning:
+
+```shell
+git clone https://github.com/openresty/lua-nginx-module.git
+cd lua-nginx-module
+bash util/run-ci.sh
+```
+
 Test Suite
 ==========
 
@@ -1026,7 +1046,6 @@ To run the whole test suite in the defau
     export PATH=/path/to/your/nginx/sbin:$PATH
     prove -I/path/to/test-nginx/lib -r t
 
-
 To run specific test files:
 
     cd /path/to/lua-nginx-module
@@ -1153,6 +1172,8 @@ Directives
 * [ssl_session_fetch_by_lua_file](#ssl_session_fetch_by_lua_file)
 * [ssl_session_store_by_lua_block](#ssl_session_store_by_lua_block)
 * [ssl_session_store_by_lua_file](#ssl_session_store_by_lua_file)
+* [proxy_ssl_verify_by_lua_block](#proxy_ssl_verify_by_lua_block)
+* [proxy_ssl_verify_by_lua_file](#proxy_ssl_verify_by_lua_file)
 * [lua_shared_dict](#lua_shared_dict)
 * [lua_socket_connect_timeout](#lua_socket_connect_timeout)
 * [lua_socket_send_timeout](#lua_socket_send_timeout)
@@ -1169,7 +1190,9 @@ Directives
 * [lua_ssl_certificate_key](#lua_ssl_certificate_key)
 * [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate)
 * [lua_ssl_verify_depth](#lua_ssl_verify_depth)
+* [lua_ssl_key_log](#lua_ssl_key_log)
 * [lua_ssl_conf_command](#lua_ssl_conf_command)
+* [lua_upstream_skip_openssl_default_verify](#lua_upstream_skip_openssl_default_verify)
 * [lua_http10_buffering](#lua_http10_buffering)
 * [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone)
 * [access_by_lua_no_postpone](#access_by_lua_no_postpone)
@@ -2860,6 +2883,8 @@ patches to the standard Nginx core:
 
 <https://openresty.org/en/nginx-ssl-patches.html>
 
+**Note for HTTP/3 (QUIC) users**: When using this directive with HTTP/3 connections, certain yield operations may fail if the QUIC SSL Lua yield patch is not applied to your OpenSSL installation. OpenResty packages include this patch by default, but if you are building lua-nginx-module separately, you may need to apply the patch manually to ensure proper yield/resume functionality for HTTP/3 connections in SSL Lua phases. The patch can be found at: [nginx-1.27.1-quic_ssl_lua_yield.patch](https://github.com/openresty/openresty/blob/master/patches/nginx/1.27.1/nginx-1.27.1-quic_ssl_lua_yield.patch)
+
 This directive was first introduced in the `v0.10.21` release.
 
 [Back to TOC](#directives)
@@ -2877,6 +2902,8 @@ Equivalent to [ssl_client_hello_by_lua_b
 
 When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server.
 
+**Note for HTTP/3 (QUIC) users**: When using this directive with HTTP/3 connections, certain yield operations may fail if the QUIC SSL Lua yield patch is not applied to your OpenSSL installation. OpenResty packages include this patch by default, but if you are building lua-nginx-module separately, you may need to apply the patch manually to ensure proper yield/resume functionality for HTTP/3 connections in SSL Lua phases. The patch can be found at: [nginx-1.27.1-quic_ssl_lua_yield.patch](https://github.com/openresty/openresty/blob/master/patches/nginx/1.27.1/nginx-1.27.1-quic_ssl_lua_yield.patch)
+
 This directive was first introduced in the `v0.10.21` release.
 
 [Back to TOC](#directives)
@@ -2970,6 +2997,8 @@ patches to the standard Nginx core:
 
 <https://openresty.org/en/nginx-ssl-patches.html>
 
+**Note for HTTP/3 (QUIC) users**: When using this directive with HTTP/3 connections, certain yield operations may fail if the QUIC SSL Lua yield patch is not applied to your OpenSSL installation. OpenResty packages include this patch by default, but if you are building lua-nginx-module separately, you may need to apply the patch manually to ensure proper yield/resume functionality for HTTP/3 connections in SSL Lua phases. The patch can be found at: [nginx-1.27.1-quic_ssl_lua_yield.patch](https://github.com/openresty/openresty/blob/master/patches/nginx/1.27.1/nginx-1.27.1-quic_ssl_lua_yield.patch)
+
 This directive was first introduced in the `v0.10.0` release.
 
 [Back to TOC](#directives)
@@ -2987,6 +3016,8 @@ Equivalent to [ssl_certificate_by_lua_bl
 
 When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server.
 
+**Note for HTTP/3 (QUIC) users**: When using this directive with HTTP/3 connections, certain yield operations may fail if the QUIC SSL Lua yield patch is not applied to your OpenSSL installation. OpenResty packages include this patch by default, but if you are building lua-nginx-module separately, you may need to apply the patch manually to ensure proper yield/resume functionality for HTTP/3 connections in SSL Lua phases. The patch can be found at: [nginx-1.27.1-quic_ssl_lua_yield.patch](https://github.com/openresty/openresty/blob/master/patches/nginx/1.27.1/nginx-1.27.1-quic_ssl_lua_yield.patch)
+
 This directive was first introduced in the `v0.10.0` release.
 
 [Back to TOC](#directives)
@@ -3138,6 +3169,84 @@ Note that: this directive is only allowe
 
 [Back to TOC](#directives)
 
+proxy_ssl_verify_by_lua_block
+-----------------------------
+
+**syntax:** *proxy_ssl_verify_by_lua_block { lua-script }*
+
+**context:** *location*
+
+**phase:** *right-after-server-certificate-message-was-processed*
+
+This directive runs user Lua code when Nginx is about to post-process the SSL server certificate message for the upstream SSL (https) connections.
+
+It is particularly useful to parse upstream server certificate and do some custom operations in pure lua.
+
+The [ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) Lua modules provided by the [lua-resty-core](https://github.com/openresty/lua-resty-core/#readme)
+library are particularly useful in this context.
+
+Below is a trivial example using the
+[ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md) module
+at the same time:
+
+```nginx
+
+ server {
+     listen 443 ssl;
+     server_name   test.com;
+     ssl_certificate /path/to/cert.crt;
+     ssl_certificate_key /path/to/key.key;
+
+     location /t {
+         proxy_ssl_certificate /path/to/cert.crt;
+         proxy_ssl_certificate_key /path/to/key.key;
+         proxy_pass https://upstream;
+
+         proxy_ssl_verify_by_lua_block {
+             local proxy_ssl_vfy = require "ngx.ssl.proxysslverify"
+             local cert = proxy_ssl_vfy.get_verify_cert()
+
+             -- ocsp to verify cert
+             -- check crl
+             proxy_ssl_vfy.set_verify_result()
+             ...
+         }
+     }
+     ...
+ }
+```
+
+See more information in the [ngx.ssl.proxysslverify](https://github.com/openresty/lua-resty-core/blob/master/lib/ngx/ssl/proxysslverify.md)
+Lua modules' official documentation.
+
+Uncaught Lua exceptions in the user Lua code immediately abort the current SSL session, so does the
+[ngx.exit](#ngxexit) call with an error code like `ngx.ERROR`.
+
+This Lua code execution context *does* support yielding, so Lua APIs that may yield
+(like cosockets, sleeping, and "light threads")
+are enabled in this context
+
+Note, `ngx.ctx` in proxy_ssl_verify_by_lua_block is belonging to upstream connection, not downstream connection, so it's different from `ngx.ctx` in contexts like ssl_certificate_by_lua etc.
+
+This directive requires OpenSSL 3.0.2 or greater.
+
+[Back to TOC](#directives)
+
+proxy_ssl_verify_by_lua_file
+----------------------------
+
+**syntax:** *proxy_ssl_verify_by_lua_file &lt;path-to-lua-script-file&gt;*
+
+**context:** *location*
+
+**phase:** *right-after-server-certificate-message-was-processed*
+
+Equivalent to [proxy_ssl_verify_by_lua_block](#proxy_ssl_verify_by_lua_block), except that the file specified by `<path-to-lua-script-file>` contains the Lua code, or, as from the `v0.5.0rc32` release, the [LuaJIT bytecode](#luajit-bytecode-support) to be executed.
+
+When a relative path like `foo/bar.lua` is given, they will be turned into the absolute path relative to the `server prefix` path determined by the `-p PATH` command-line option while starting the Nginx server.
+
+[Back to TOC](#directives)
+
 lua_shared_dict
 ---------------
 
@@ -3430,6 +3539,19 @@ See also [lua_ssl_certificate](#lua_ssl_
 
 [Back to TOC](#directives)
 
+lua_ssl_key_log
+---------------
+
+**syntax:** *lua_ssl_key_log &lt;file&gt;*
+
+**default:** *none*
+
+**context:** *http, server, location*
+
+Enables logging of client connection SSL keys in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method and specifies the path to the key log file. Keys are logged in the SSLKEYLOGFILE format compatible with Wireshark.
+
+[Back to TOC](#directives)
+
 lua_ssl_conf_command
 --------------------
 
@@ -3461,6 +3583,21 @@ This directive was first introduced in t
 
 [Back to TOC](#directives)
 
+lua_upstream_skip_openssl_default_verify
+--------------------
+
+**syntax:** *lua_upstream_skip_openssl_default_verify on|off*
+
+**default:** *lua_upstream_skip_openssl_default_verify off*
+
+**context:** *location, location-if*
+
+When using proxy_ssl_verify_by_lua directive, `lua_upstream_skip_openssl_default_verify` controls whether to skip default openssl's verify function, that means using pure Lua code to verify upstream server certificate.
+
+This directive is turned `off` by default.
+
+[Back to TOC](#directives)
+
 lua_http10_buffering
 --------------------
 
@@ -3751,6 +3888,7 @@ Nginx API for Lua
 * [ngx.socket.tcp](#ngxsockettcp)
 * [tcpsock:bind](#tcpsockbind)
 * [tcpsock:connect](#tcpsockconnect)
+* [tcpsock:getfd](#getfd)
 * [tcpsock:setclientcert](#tcpsocksetclientcert)
 * [tcpsock:sslhandshake](#tcpsocksslhandshake)
 * [tcpsock:send](#tcpsocksend)
@@ -7854,13 +7992,14 @@ See also [ngx.socket.udp](#ngxsocketudp)
 
 tcpsock:bind
 ------------
-**syntax:** *ok, err = tcpsock:bind(address)*
+**syntax:** *ok, err = tcpsock:bind(address, port?)*
 
 **context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;,ssl_session_fetch_by_lua&#42;,ssl_client_hello_by_lua&#42;*
 
 Just like the standard [proxy_bind](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_bind) directive, this api makes the outgoing connection to a upstream server originate from the specified local IP address.
 
-Only IP addresses can be specified as the `address` argument.
+IP addresses can be specified as the `address` argument.
+The optional `port` argument is usually used in the transparent proxy.
 
 Here is an example for connecting to a TCP server from the specified local IP address:
 
@@ -8010,6 +8149,21 @@ This method was first introduced in the
 
 [Back to TOC](#nginx-api-for-lua)
 
+
+tcpsock:getfd
+--------------------
+
+**syntax:** *fd, err = tcpsock:getfd()*
+
+**context:** *rewrite_by_lua&#42;, access_by_lua&#42;, content_by_lua&#42;, ngx.timer.&#42;, ssl_certificate_by_lua&#42;, ssl_session_fetch_by_lua&#42;, ssl_client_hello_by_lua&#42;*
+
+Get the file descriptor of the current tcp socket.
+
+This method was first introduced in the `v0.10.29` release.
+
+[Back to TOC](#nginx-api-for-lua)
+
+
 tcpsock:setclientcert
 ---------------------
 
diff -pruN 1:0.10.28-3/config 1:0.10.29-1/config
--- 1:0.10.28-3/config	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/config	2025-12-07 15:22:42.000000000 +0000
@@ -296,6 +296,7 @@ HTTP_LUA_SRCS=" \
             $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.c \
             $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.c \
             $ngx_addon_dir/src/ngx_http_lua_ssl.c \
+            $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_verifyby.c \
             $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.c \
             $ngx_addon_dir/src/ngx_http_lua_input_filters.c \
             $ngx_addon_dir/src/ngx_http_lua_pipe.c \
@@ -359,6 +360,7 @@ HTTP_LUA_DEPS=" \
             $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.h \
             $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.h \
             $ngx_addon_dir/src/ngx_http_lua_ssl.h \
+            $ngx_addon_dir/src/ngx_http_lua_proxy_ssl_verifyby.h \
             $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.h \
             $ngx_addon_dir/src/ngx_http_lua_input_filters.h \
             $ngx_addon_dir/src/ngx_http_lua_pipe.h \
diff -pruN 1:0.10.28-3/debian/changelog 1:0.10.29-1/debian/changelog
--- 1:0.10.28-3/debian/changelog	2025-08-30 10:08:29.000000000 +0000
+++ 1:0.10.29-1/debian/changelog	2025-12-07 16:17:32.000000000 +0000
@@ -1,3 +1,14 @@
+libnginx-mod-http-lua (1:0.10.29-1) unstable; urgency=medium
+
+  * New upstream version 0.10.29
+  * d/gbp.conf: dist = DEP14, debian-branch = debian/latest
+  * d/watch: rewrite to version 5, use Github uscan-template
+  * d/gbp.conf: upstream-branch = upstream/latest
+  * d/control: updated lua-resty-core dependency (>=0.1.32)
+  * d/control: remove Rules-Requires-Root: no
+
+ -- Jan Mojžíš <janmojzis@debian.org>  Sun, 07 Dec 2025 17:17:32 +0100
+
 libnginx-mod-http-lua (1:0.10.28-3) unstable; urgency=medium
 
   * d/control: add loong64 hurd-i386 hurd-amd64 architectures
diff -pruN 1:0.10.28-3/debian/control 1:0.10.29-1/debian/control
--- 1:0.10.28-3/debian/control	2025-08-30 10:08:29.000000000 +0000
+++ 1:0.10.29-1/debian/control	2025-12-07 16:17:32.000000000 +0000
@@ -11,12 +11,11 @@ Standards-Version: 4.7.2
 Homepage: https://github.com/openresty/lua-nginx-module
 Vcs-Git: https://salsa.debian.org/nginx-team/libnginx-mod-http-lua.git
 Vcs-Browser: https://salsa.debian.org/nginx-team/libnginx-mod-http-lua
-Rules-Requires-Root: no
 
 Package: libnginx-mod-http-lua
 Architecture: amd64 arm64 armel armhf i386 mips64el mipsel s390x powerpc riscv64 loong64 hurd-i386 hurd-amd64
 Multi-Arch: foreign
-Depends: lua-resty-core (>=0.1.31),
+Depends: lua-resty-core (>=0.1.32),
          ${misc:Depends},
          ${shlibs:Depends},
 Recommends: nginx,
diff -pruN 1:0.10.28-3/debian/gbp.conf 1:0.10.29-1/debian/gbp.conf
--- 1:0.10.28-3/debian/gbp.conf	2025-08-30 10:08:29.000000000 +0000
+++ 1:0.10.29-1/debian/gbp.conf	2025-12-07 16:17:32.000000000 +0000
@@ -1,9 +1,10 @@
 [DEFAULT]
-debian-branch = main
-upstream-branch = upstream
+debian-branch = debian/latest
+upstream-branch = upstream/latest
 upstream-tag = upstream/%(version)s
 pristine-tar = True
 sign-tags = True
+dist = DEP14
 
 [import-orig]
 merge-mode = replace
diff -pruN 1:0.10.28-3/debian/watch 1:0.10.29-1/debian/watch
--- 1:0.10.28-3/debian/watch	2025-08-30 10:08:29.000000000 +0000
+++ 1:0.10.29-1/debian/watch	2025-12-07 16:17:32.000000000 +0000
@@ -1,6 +1,6 @@
-version=4
-opts="\
-uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha|a|b)\d*)$/$1~$2/,\
-" \
-https://github.com/openresty/lua-nginx-module/tags \
-(?:.*?/)?v?@ANY_VERSION@@ARCHIVE_EXT@
+Version: 5
+Template: Github
+Owner: openresty
+Project: lua-nginx-module
+Uversion-Mangle: s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha|a|b)\d*)$/$1~$2/
+Pgp-Mode: none
diff -pruN 1:0.10.28-3/doc/HttpLuaModule.wiki 1:0.10.29-1/doc/HttpLuaModule.wiki
--- 1:0.10.28-3/doc/HttpLuaModule.wiki	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/doc/HttpLuaModule.wiki	2025-12-07 15:22:42.000000000 +0000
@@ -2925,6 +2925,16 @@ This directive was first introduced in t
 
 See also [[#lua_ssl_certificate|lua_ssl_certificate]], [[#lua_ssl_certificate_key|lua_ssl_certificate_key]] and [[#lua_ssl_trusted_certificate|lua_ssl_trusted_certificate]].
 
+== lua_ssl_key_log ==
+
+'''syntax:''' ''lua_ssl_key_log <file>''
+
+'''default:''' ''none''
+
+'''context:''' ''http, server, location''
+
+Enables logging of client connection SSL keys in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method and specifies the path to the key log file. Keys are logged in the SSLKEYLOGFILE format compatible with Wireshark.
+
 == lua_ssl_conf_command ==
 
 '''syntax:''' ''lua_ssl_conf_command <command>''
@@ -2950,6 +2960,19 @@ Note though that configuring OpenSSL dir
 
 This directive was first introduced in the <code>v0.10.21</code> release.
 
+== lua_upstream_skip_openssl_default_verify ==
+
+'''syntax:''' ''lua_upstream_skip_openssl_default_verify on|off''
+
+'''default:''' ''lua_upstream_skip_openssl_default_verify off''
+
+'''context:''' ''location, location-if''
+
+When using proxy_ssl_verify_by_lua directive, `lua_upstream_skip_openssl_default_verify` controls whether to skip default openssl's verify function, that means using pure Lua code to verify upstream server certificate.
+
+This directive is turned <code>off</code> by default.
+
+[Back to TOC](#directives)
 
 
 == lua_http10_buffering ==
diff -pruN 1:0.10.28-3/src/api/ngx_http_lua_api.h 1:0.10.29-1/src/api/ngx_http_lua_api.h
--- 1:0.10.28-3/src/api/ngx_http_lua_api.h	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/api/ngx_http_lua_api.h	2025-12-07 15:22:42.000000000 +0000
@@ -19,7 +19,8 @@
 /* Public API for other Nginx modules */
 
 
-#define ngx_http_lua_version  10028
+#define ngx_http_lua_version  10029
+#define NGX_HTTP_LUA_EXPORT_CO_CTX_CLEANUP 1
 
 
 typedef struct ngx_http_lua_co_ctx_s  ngx_http_lua_co_ctx_t;
@@ -66,6 +67,13 @@ void ngx_http_lua_set_cur_co_ctx(ngx_htt
 
 lua_State *ngx_http_lua_get_co_ctx_vm(ngx_http_lua_co_ctx_t *coctx);
 
+
+void *ngx_http_lua_get_co_ctx_data(ngx_http_lua_co_ctx_t *coctx);
+void ngx_http_lua_set_co_ctx_cleanup(ngx_http_lua_co_ctx_t *coctx,
+    ngx_http_cleanup_pt cleanup, void *data);
+void ngx_http_lua_cleanup_co_ctx_pending_operation(
+    ngx_http_lua_co_ctx_t *coctx);
+
 void ngx_http_lua_co_ctx_resume_helper(ngx_http_lua_co_ctx_t *coctx, int nrets);
 
 int ngx_http_lua_get_lua_http10_buffering(ngx_http_request_t *r);
diff -pruN 1:0.10.28-3/src/ngx_http_lua_accessby.c 1:0.10.29-1/src/ngx_http_lua_accessby.c
--- 1:0.10.28-3/src/ngx_http_lua_accessby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_accessby.c	2025-12-07 15:22:42.000000000 +0000
@@ -95,6 +95,7 @@ ngx_http_lua_access_handler(ngx_http_req
 
     dd("entered? %d", (int) ctx->entered_access_phase);
 
+
     if (ctx->entered_access_phase) {
         dd("calling wev handler");
         rc = ctx->resume_handler(r);
@@ -105,7 +106,9 @@ ngx_http_lua_access_handler(ngx_http_req
         }
 
         if (rc == NGX_OK) {
-            if (r->header_sent) {
+            if (r->header_sent
+                || (r->headers_out.status != 0 && ctx->out != NULL))
+            {
                 dd("header already sent");
 
                 /* response header was already generated in access_by_lua*,
@@ -170,6 +173,10 @@ ngx_http_lua_access_handler_inline(ngx_h
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->access_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua inline script (w/ cache) sp = 1 */
     rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
                                        llcf->access_src.value.data,
@@ -211,6 +218,10 @@ ngx_http_lua_access_handler_file(ngx_htt
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->access_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua script file (w/ cache)        sp = 1 */
     rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
                                      &llcf->access_src_ref,
@@ -369,7 +380,8 @@ ngx_http_lua_access_by_chunk(lua_State *
 
 #if 1
     if (rc == NGX_OK) {
-        if (r->header_sent) {
+        if (r->header_sent || (r->headers_out.status != 0 && ctx->out != NULL))
+        {
             dd("header already sent");
 
             /* response header was already generated in access_by_lua*,
diff -pruN 1:0.10.28-3/src/ngx_http_lua_api.c 1:0.10.29-1/src/ngx_http_lua_api.c
--- 1:0.10.28-3/src/ngx_http_lua_api.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_api.c	2025-12-07 15:22:42.000000000 +0000
@@ -221,7 +221,7 @@ ngx_http_lua_get_cur_co_ctx(ngx_http_req
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
 
-    return ctx->cur_co_ctx;
+    return ctx == NULL ? NULL : ctx->cur_co_ctx;
 }
 
 
@@ -245,6 +245,29 @@ ngx_http_lua_get_co_ctx_vm(ngx_http_lua_
 }
 
 
+void *
+ngx_http_lua_get_co_ctx_data(ngx_http_lua_co_ctx_t *coctx)
+{
+    return coctx ? coctx->data : NULL;
+}
+
+
+void
+ngx_http_lua_set_co_ctx_cleanup(ngx_http_lua_co_ctx_t *coctx,
+    ngx_http_cleanup_pt cleanup, void *data)
+{
+    coctx->cleanup = cleanup;
+    coctx->data = data;
+}
+
+
+void
+ngx_http_lua_cleanup_co_ctx_pending_operation(ngx_http_lua_co_ctx_t *coctx)
+{
+    ngx_http_lua_cleanup_pending_operation(coctx);
+}
+
+
 static ngx_int_t
 ngx_http_lua_co_ctx_resume(ngx_http_request_t *r)
 {
diff -pruN 1:0.10.28-3/src/ngx_http_lua_balancer.c 1:0.10.29-1/src/ngx_http_lua_balancer.c
--- 1:0.10.28-3/src/ngx_http_lua_balancer.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_balancer.c	2025-12-07 15:22:42.000000000 +0000
@@ -702,7 +702,7 @@ ngx_http_lua_balancer_free_peer(ngx_peer
                     item->host.len = host->len;
 
                 } else {
-                    item->host.data = ngx_pstrdup(c->pool, bp->addr_text);
+                    item->host.data = ngx_pstrdup(c->pool, host);
                     if (item->host.data == NULL) {
                         ngx_http_lua_balancer_close(c);
 
@@ -713,7 +713,7 @@ ngx_http_lua_balancer_free_peer(ngx_peer
                         return;
                     }
 
-                    item->host.len = bp->addr_text->len;
+                    item->host.len = host->len;
                 }
 
             } else {
diff -pruN 1:0.10.28-3/src/ngx_http_lua_bodyfilterby.c 1:0.10.29-1/src/ngx_http_lua_bodyfilterby.c
--- 1:0.10.28-3/src/ngx_http_lua_bodyfilterby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_bodyfilterby.c	2025-12-07 15:22:42.000000000 +0000
@@ -155,6 +155,10 @@ ngx_http_lua_body_filter_inline(ngx_http
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->body_filter_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua inline script (w/ cache) sp = 1 */
     rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
                                        llcf->body_filter_src.value.data,
@@ -206,6 +210,10 @@ ngx_http_lua_body_filter_file(ngx_http_r
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->body_filter_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua script file (w/ cache)        sp = 1 */
     rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
                                      &llcf->body_filter_src_ref,
@@ -368,6 +376,7 @@ ngx_http_lua_body_filter(ngx_http_reques
         }
 
     } else {
+        ctx->context = old_context;
         out = NULL;
     }
 
diff -pruN 1:0.10.28-3/src/ngx_http_lua_common.h 1:0.10.29-1/src/ngx_http_lua_common.h
--- 1:0.10.28-3/src/ngx_http_lua_common.h	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_common.h	2025-12-07 15:22:42.000000000 +0000
@@ -131,23 +131,27 @@ typedef struct {
     (NGX_HTTP_LUA_FILE_TAG_LEN + 2 * MD5_DIGEST_LENGTH)
 
 
-/* must be within 16 bit */
-#define NGX_HTTP_LUA_CONTEXT_SET                0x0001
-#define NGX_HTTP_LUA_CONTEXT_REWRITE            0x0002
-#define NGX_HTTP_LUA_CONTEXT_ACCESS             0x0004
-#define NGX_HTTP_LUA_CONTEXT_CONTENT            0x0008
-#define NGX_HTTP_LUA_CONTEXT_LOG                0x0010
-#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER      0x0020
-#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER        0x0040
-#define NGX_HTTP_LUA_CONTEXT_TIMER              0x0080
-#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER        0x0100
-#define NGX_HTTP_LUA_CONTEXT_BALANCER           0x0200
-#define NGX_HTTP_LUA_CONTEXT_SSL_CERT           0x0400
-#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE     0x0800
-#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH     0x1000
-#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER        0x2000
-#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO   0x4000
-#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE     0x8000
+/* must be within 32 bits */
+#define NGX_HTTP_LUA_CONTEXT_SET                0x00000001
+#define NGX_HTTP_LUA_CONTEXT_REWRITE            0x00000002
+#define NGX_HTTP_LUA_CONTEXT_ACCESS             0x00000004
+#define NGX_HTTP_LUA_CONTEXT_CONTENT            0x00000008
+#define NGX_HTTP_LUA_CONTEXT_LOG                0x00000010
+#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER      0x00000020
+#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER        0x00000040
+#define NGX_HTTP_LUA_CONTEXT_TIMER              0x00000080
+#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER        0x00000100
+#define NGX_HTTP_LUA_CONTEXT_BALANCER           0x00000200
+#define NGX_HTTP_LUA_CONTEXT_SSL_CERT           0x00000400
+#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE     0x00000800
+#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH     0x00001000
+#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER        0x00002000
+#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO   0x00004000
+#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE     0x00008000
+
+#ifdef HAVE_PROXY_SSL_PATCH
+#define NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY   0x00010000
+#endif
 
 
 #define NGX_HTTP_LUA_FFI_NO_REQ_CTX         -100
@@ -171,6 +175,8 @@ typedef struct ngx_http_lua_srv_conf_s
 
 typedef struct ngx_http_lua_main_conf_s  ngx_http_lua_main_conf_t;
 
+typedef struct ngx_http_lua_loc_conf_s  ngx_http_lua_loc_conf_t;
+
 typedef struct ngx_http_lua_header_val_s  ngx_http_lua_header_val_t;
 
 typedef struct ngx_http_lua_posted_thread_s  ngx_http_lua_posted_thread_t;
@@ -184,6 +190,9 @@ typedef ngx_int_t (*ngx_http_lua_main_co
 typedef ngx_int_t (*ngx_http_lua_srv_conf_handler_pt)(ngx_http_request_t *r,
     ngx_http_lua_srv_conf_t *lscf, lua_State *L);
 
+typedef ngx_int_t (*ngx_http_lua_loc_conf_handler_pt)(ngx_http_request_t *r,
+    ngx_http_lua_loc_conf_t *llcf, lua_State *L);
+
 typedef ngx_int_t (*ngx_http_lua_set_header_pt)(ngx_http_request_t *r,
     ngx_http_lua_header_val_t *hv, ngx_str_t *value);
 
@@ -369,7 +378,7 @@ struct ngx_http_lua_srv_conf_s {
 };
 
 
-typedef struct {
+struct ngx_http_lua_loc_conf_s {
 #if (NGX_HTTP_SSL)
     ngx_ssl_t              *ssl;  /* shared by SSL cosockets */
     ngx_array_t            *ssl_certificates;
@@ -379,9 +388,20 @@ typedef struct {
     ngx_uint_t              ssl_verify_depth;
     ngx_str_t               ssl_trusted_certificate;
     ngx_str_t               ssl_crl;
+    ngx_str_t               ssl_key_log;
 #if (nginx_version >= 1019004)
     ngx_array_t            *ssl_conf_commands;
 #endif
+
+#ifdef HAVE_PROXY_SSL_PATCH
+    ngx_http_lua_loc_conf_handler_pt       proxy_ssl_verify_handler;
+    ngx_str_t                              proxy_ssl_verify_src;
+    u_char                                *proxy_ssl_verify_src_key;
+    u_char                                *proxy_ssl_verify_chunkname;
+    int                                    proxy_ssl_verify_src_ref;
+    ngx_flag_t                             upstream_skip_openssl_default_verify;
+#endif
+
 #endif
 
     ngx_flag_t              force_read_body; /* whether force request body to
@@ -463,7 +483,7 @@ typedef struct {
     ngx_flag_t                       log_socket_errors;
     ngx_flag_t                       check_client_abort;
     ngx_flag_t                       use_default_type;
-} ngx_http_lua_loc_conf_t;
+};
 
 
 typedef enum {
@@ -627,7 +647,7 @@ typedef struct ngx_http_lua_ctx_s {
 
     int                      uthreads; /* number of active user threads */
 
-    uint16_t                 context;   /* the current running directive context
+    uint32_t                 context;   /* the current running directive context
                                            (or running phase) for the current
                                            Lua chunk */
 
diff -pruN 1:0.10.28-3/src/ngx_http_lua_contentby.c 1:0.10.29-1/src/ngx_http_lua_contentby.c
--- 1:0.10.28-3/src/ngx_http_lua_contentby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_contentby.c	2025-12-07 15:22:42.000000000 +0000
@@ -267,6 +267,10 @@ ngx_http_lua_content_handler_file(ngx_ht
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->content_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua script file (w/ cache)        sp = 1 */
     rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
                                      &llcf->content_src_ref,
@@ -297,6 +301,10 @@ ngx_http_lua_content_handler_inline(ngx_
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->content_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua inline script (w/ cache) sp = 1 */
     rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
                                        llcf->content_src.value.data,
diff -pruN 1:0.10.28-3/src/ngx_http_lua_control.c 1:0.10.29-1/src/ngx_http_lua_control.c
--- 1:0.10.28-3/src/ngx_http_lua_control.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_control.c	2025-12-07 15:22:42.000000000 +0000
@@ -384,6 +384,9 @@ ngx_http_lua_ffi_exit(ngx_http_request_t
                                        | NGX_HTTP_LUA_CONTEXT_TIMER
                                        | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER
                                        | NGX_HTTP_LUA_CONTEXT_BALANCER
+#ifdef HAVE_PROXY_SSL_PATCH
+                                       | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY
+#endif
                                        | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO
                                        | NGX_HTTP_LUA_CONTEXT_SSL_CERT
                                        | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
@@ -395,6 +398,9 @@ ngx_http_lua_ffi_exit(ngx_http_request_t
     }
 
     if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT
+#ifdef HAVE_PROXY_SSL_PATCH
+                        | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY
+#endif
                         | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO
                         | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE
                         | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH))
diff -pruN 1:0.10.28-3/src/ngx_http_lua_headerfilterby.c 1:0.10.29-1/src/ngx_http_lua_headerfilterby.c
--- 1:0.10.28-3/src/ngx_http_lua_headerfilterby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_headerfilterby.c	2025-12-07 15:22:42.000000000 +0000
@@ -165,6 +165,10 @@ ngx_http_lua_header_filter_inline(ngx_ht
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->header_filter_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua inline script (w/ cache) sp = 1 */
     rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
                                        llcf->header_filter_src.value.data,
@@ -210,6 +214,10 @@ ngx_http_lua_header_filter_file(ngx_http
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->header_filter_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua script file (w/ cache)        sp = 1 */
     rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
                                      &llcf->header_filter_src_ref,
diff -pruN 1:0.10.28-3/src/ngx_http_lua_logby.c 1:0.10.29-1/src/ngx_http_lua_logby.c
--- 1:0.10.28-3/src/ngx_http_lua_logby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_logby.c	2025-12-07 15:22:42.000000000 +0000
@@ -149,6 +149,10 @@ ngx_http_lua_log_handler_inline(ngx_http
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->log_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua inline script (w/ cache) sp = 1 */
     rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
                                        llcf->log_src.value.data,
@@ -188,6 +192,10 @@ ngx_http_lua_log_handler_file(ngx_http_r
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->log_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua script file (w/ cache)        sp = 1 */
     rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
                                      &llcf->log_src_ref,
diff -pruN 1:0.10.28-3/src/ngx_http_lua_module.c 1:0.10.29-1/src/ngx_http_lua_module.c
--- 1:0.10.28-3/src/ngx_http_lua_module.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_module.c	2025-12-07 15:22:42.000000000 +0000
@@ -31,6 +31,11 @@
 #include "ngx_http_lua_ssl_certby.h"
 #include "ngx_http_lua_ssl_session_storeby.h"
 #include "ngx_http_lua_ssl_session_fetchby.h"
+
+#ifdef HAVE_PROXY_SSL_PATCH
+#include "ngx_http_lua_proxy_ssl_verifyby.h"
+#endif
+
 #include "ngx_http_lua_headers.h"
 #include "ngx_http_lua_headers_out.h"
 #if !(NGX_WIN32)
@@ -54,6 +59,11 @@ static ngx_int_t ngx_http_lua_merge_ssl(
     ngx_http_lua_loc_conf_t *conf, ngx_http_lua_loc_conf_t *prev);
 static ngx_int_t ngx_http_lua_set_ssl(ngx_conf_t *cf,
     ngx_http_lua_loc_conf_t *llcf);
+static void key_log_callback(const ngx_ssl_conn_t *ssl_conn,
+    const char *line);
+static void ngx_http_lua_ssl_cleanup_key_log(void *data);
+static ngx_int_t ngx_http_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl,
+    ngx_str_t *file);
 #if (nginx_version >= 1019004)
 static char *ngx_http_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post,
     void *data);
@@ -655,6 +665,30 @@ static ngx_command_t ngx_http_lua_cmds[]
       0,
       (void *) ngx_http_lua_ssl_sess_fetch_handler_file },
 
+#ifdef HAVE_PROXY_SSL_PATCH
+    /* same context as proxy_pass directive */
+    { ngx_string("proxy_ssl_verify_by_lua_block"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+      ngx_http_lua_proxy_ssl_verify_by_lua_block,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      (void *) ngx_http_lua_proxy_ssl_verify_handler_inline },
+
+    { ngx_string("proxy_ssl_verify_by_lua_file"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+      ngx_http_lua_proxy_ssl_verify_by_lua,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      (void *) ngx_http_lua_proxy_ssl_verify_handler_file },
+
+    { ngx_string("lua_upstream_skip_openssl_default_verify"),
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_lua_loc_conf_t, upstream_skip_openssl_default_verify),
+      NULL },
+#endif
+
     { ngx_string("lua_ssl_verify_depth"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_num_slot,
@@ -690,6 +724,13 @@ static ngx_command_t ngx_http_lua_cmds[]
       offsetof(ngx_http_lua_loc_conf_t, ssl_crl),
       NULL },
 
+    { ngx_string("lua_ssl_key_log"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_lua_loc_conf_t, ssl_key_log),
+      NULL },
+
 #if (nginx_version >= 1019004)
     { ngx_string("lua_ssl_conf_command"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
@@ -1030,6 +1071,13 @@ ngx_http_lua_create_main_conf(ngx_conf_t
      *      lmcf->shm_zones = NULL;
      *      lmcf->init_handler = NULL;
      *      lmcf->init_src = { 0, NULL };
+     *      lmcf->init_chunkname = NULL;
+     *      lmcf->init_worker_handler = NULL;
+     *      lmcf->init_worker_src = { 0, NULL };
+     *      lmcf->init_worker_chunkname = NULL;
+     *      lmcf->exit_worker_handler = NULL;
+     *      lmcf->exit_worker_src = { 0, NULL };
+     *      lmcf->exit_worker_chunkname = NULL;
      *      lmcf->shm_zones_inited = 0;
      *      lmcf->shdict_zones = NULL;
      *      lmcf->preload_hooks = NULL;
@@ -1197,6 +1245,11 @@ ngx_http_lua_create_srv_conf(ngx_conf_t
      *      lscf->srv.ssl_sess_fetch_chunkname = NULL;
      *      lscf->srv.ssl_sess_fetch_src_key = NULL;
      *
+     *      lscf->srv.server_rewrite_handler = NULL;
+     *      lscf->srv.server_rewrite_src = { 0, NULL };
+     *      lscf->srv.server_rewrite_chunkname = NULL;
+     *      lscf->srv.server_rewrite_src_key = NULL;
+     *
      *      lscf->balancer.original_init_upstream = NULL;
      *      lscf->balancer.original_init_peer = NULL;
      *      lscf->balancer.handler = NULL;
@@ -1212,6 +1265,7 @@ ngx_http_lua_create_srv_conf(ngx_conf_t
     lscf->srv.ssl_sess_fetch_src_ref = LUA_REFNIL;
 #endif
 
+    lscf->srv.server_rewrite_src_ref = LUA_REFNIL;
     lscf->balancer.src_ref = LUA_REFNIL;
     lscf->balancer.max_cached = NGX_CONF_UNSET_UINT;
     return lscf;
@@ -1387,31 +1441,45 @@ ngx_http_lua_create_loc_conf(ngx_conf_t
     /* set by ngx_pcalloc:
      *      conf->access_src  = {{ 0, NULL }, NULL, NULL, NULL};
      *      conf->access_src_key = NULL
+     *      conf->access_handler = NULL;
+     *      conf->access_chunkname = NULL;
+     *
      *      conf->rewrite_src = {{ 0, NULL }, NULL, NULL, NULL};
      *      conf->rewrite_src_key = NULL;
      *      conf->rewrite_handler = NULL;
+     *      conf->rewrite_chunkname = NULL;
      *
      *      conf->content_src = {{ 0, NULL }, NULL, NULL, NULL};
      *      conf->content_src_key = NULL;
      *      conf->content_handler = NULL;
+     *      conf->content_chunkname = NULL;
      *
      *      conf->log_src = {{ 0, NULL }, NULL, NULL, NULL};
      *      conf->log_src_key = NULL;
      *      conf->log_handler = NULL;
+     *      conf->log_chunkname = NULL;
      *
      *      conf->header_filter_src = {{ 0, NULL }, NULL, NULL, NULL};
      *      conf->header_filter_src_key = NULL;
      *      conf->header_filter_handler = NULL;
+     *      conf->header_filter_chunkname = NULL;
      *
      *      conf->body_filter_src = {{ 0, NULL }, NULL, NULL, NULL};
      *      conf->body_filter_src_key = NULL;
      *      conf->body_filter_handler = NULL;
+     *      conf->body_filter_chunkname = NULL;
      *
      *      conf->ssl = 0;
      *      conf->ssl_protocols = 0;
      *      conf->ssl_ciphers = { 0, NULL };
      *      conf->ssl_trusted_certificate = { 0, NULL };
      *      conf->ssl_crl = { 0, NULL };
+     *      conf->ssl_key_log = { 0, NULL };
+     *
+     *      conf->proxy_ssl_verify_handler = NULL;
+     *      conf->proxy_ssl_verify_src = { 0, NULL };
+     *      conf->proxy_ssl_verify_chunkname = NULL;
+     *      conf->proxy_ssl_verify_src_key = NULL;
      */
 
     conf->force_read_body    = NGX_CONF_UNSET;
@@ -1445,6 +1513,10 @@ ngx_http_lua_create_loc_conf(ngx_conf_t
 #if (nginx_version >= 1019004)
     conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
 #endif
+#ifdef HAVE_PROXY_SSL_PATCH
+    conf->proxy_ssl_verify_src_ref = LUA_REFNIL;
+    conf->upstream_skip_openssl_default_verify = NGX_CONF_UNSET;
+#endif
 #endif
 
     return conf;
@@ -1532,12 +1604,32 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *
     ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
                              prev->ssl_trusted_certificate, "");
     ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
+    ngx_conf_merge_str_value(conf->ssl_key_log, prev->ssl_key_log, "");
 
 #if (nginx_version >= 1019004)
     ngx_conf_merge_ptr_value(conf->ssl_conf_commands, prev->ssl_conf_commands,
                              NULL);
 #endif
 
+#ifdef HAVE_PROXY_SSL_PATCH
+    if (conf->proxy_ssl_verify_src.len == 0) {
+        conf->proxy_ssl_verify_src = prev->proxy_ssl_verify_src;
+        conf->proxy_ssl_verify_handler = prev->proxy_ssl_verify_handler;
+        conf->proxy_ssl_verify_src_ref = prev->proxy_ssl_verify_src_ref;
+        conf->proxy_ssl_verify_src_key = prev->proxy_ssl_verify_src_key;
+        conf->proxy_ssl_verify_chunkname = prev->proxy_ssl_verify_chunkname;
+    }
+
+    if (conf->proxy_ssl_verify_src.len) {
+        if (ngx_http_lua_proxy_ssl_verify_set_callback(cf) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    ngx_conf_merge_value(conf->upstream_skip_openssl_default_verify,
+                         prev->upstream_skip_openssl_default_verify, 0);
+#endif
+
     if (ngx_http_lua_set_ssl(cf, conf) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -1595,6 +1687,7 @@ ngx_http_lua_merge_ssl(ngx_conf_t *cf,
         && conf->ssl_certificate_keys == NGX_CONF_UNSET_PTR
         && conf->ssl_trusted_certificate.data == NULL
         && conf->ssl_crl.data == NULL
+        && conf->ssl_key_log.data == NULL
 #if (nginx_version >= 1019004)
         && conf->ssl_conf_commands == NGX_CONF_UNSET_PTR
 #endif
@@ -1702,6 +1795,12 @@ ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx
         return NGX_ERROR;
     }
 
+    if (ngx_http_lua_ssl_key_log(cf, llcf->ssl, &llcf->ssl_key_log)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
 #if (nginx_version >= 1019004)
     if (ngx_ssl_conf_commands(cf, llcf->ssl, llcf->ssl_conf_commands)
         != NGX_OK)
@@ -1713,6 +1812,102 @@ ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx
     return NGX_OK;
 }
 
+
+static void
+key_log_callback(const ngx_ssl_conn_t *ssl_conn, const char *line)
+{
+    ngx_http_lua_ssl_key_log_t  *ssl_key_log;
+    ngx_connection_t            *c;
+
+    ssl_key_log = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),
+                                      ngx_http_lua_ssl_key_log_index);
+    if (ssl_key_log == NULL) {
+        c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+        ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0, "get ssl key log failed");
+
+        return;
+    }
+
+    (void) ngx_write_fd(ssl_key_log->fd, (void *) line, ngx_strlen(line));
+    (void) ngx_write_fd(ssl_key_log->fd, (void *) "\n", 1);
+}
+
+
+static void
+ngx_http_lua_ssl_cleanup_key_log(void *data)
+{
+    ngx_http_lua_ssl_key_log_t  *ssl_key_log = data;
+
+    if (ngx_close_file(ssl_key_log->fd) == NGX_FILE_ERROR) {
+        ngx_ssl_error(NGX_LOG_ALERT, ssl_key_log->ssl->log, 0,
+                      ngx_close_file_n "(\"%V\") failed", ssl_key_log->name);
+    }
+}
+
+
+static ngx_int_t
+ngx_http_lua_ssl_key_log(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
+{
+    ngx_fd_t                     fd;
+    ngx_http_lua_ssl_key_log_t  *ssl_key_log;
+    ngx_pool_cleanup_t          *cln;
+
+    if (!file->len) {
+        return NGX_OK;
+    }
+
+    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    /*
+     * append so that existing keylog file contents can be preserved
+     */
+    fd = ngx_open_file(file->data, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
+                       NGX_FILE_DEFAULT_ACCESS);
+    if (fd == NGX_INVALID_FILE) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, ngx_open_file_n
+                      "(\"%V\") failed", file);
+        return NGX_ERROR;
+    }
+
+    ssl_key_log = ngx_palloc(cf->pool, sizeof(ngx_http_lua_ssl_key_log_t));
+    if (ssl_key_log == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "ngx_pcalloc() failed");
+        return NGX_ERROR;
+    }
+
+    ssl_key_log->ssl = ssl;
+    ssl_key_log->fd = fd;
+    ssl_key_log->name = *file;
+
+    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_http_lua_ssl_key_log_index,
+                            ssl_key_log) == 0)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_set_ex_data() failed");
+        return NGX_ERROR;
+    }
+
+    cln = ngx_pool_cleanup_add(cf->pool, 0);
+    if (cln == NULL) {
+        ngx_http_lua_ssl_cleanup_key_log(ssl_key_log);
+        return NGX_ERROR;
+    }
+
+    cln->handler = ngx_http_lua_ssl_cleanup_key_log;
+    cln->data = ssl_key_log;
+
+    SSL_CTX_set_keylog_callback(ssl->ctx, key_log_callback);
+
+    return NGX_OK;
+}
+
+
 #if (nginx_version >= 1019004)
 static char *
 ngx_http_lua_ssl_conf_command_check(ngx_conf_t *cf, void *post, void *data)
diff -pruN 1:0.10.28-3/src/ngx_http_lua_proxy_ssl_verifyby.c 1:0.10.29-1/src/ngx_http_lua_proxy_ssl_verifyby.c
--- 1:0.10.28-3/src/ngx_http_lua_proxy_ssl_verifyby.c	1970-01-01 00:00:00.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_proxy_ssl_verifyby.c	2025-12-07 15:22:42.000000000 +0000
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+#ifndef DDEBUG
+#define DDEBUG 0
+#endif
+#include "ddebug.h"
+
+
+#if (NGX_HTTP_SSL)
+
+
+#include "ngx_http_lua_cache.h"
+#include "ngx_http_lua_initworkerby.h"
+#include "ngx_http_lua_util.h"
+#include "ngx_http_ssl_module.h"
+#include "ngx_http_lua_contentby.h"
+#include "ngx_http_lua_directive.h"
+#include "ngx_http_lua_ssl.h"
+
+#ifdef HAVE_PROXY_SSL_PATCH
+#include "ngx_http_lua_proxy_ssl_verifyby.h"
+
+
+#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x30000020uL)
+static void ngx_http_lua_proxy_ssl_verify_done(void *data);
+static void ngx_http_lua_proxy_ssl_verify_aborted(void *data);
+#endif
+static ngx_int_t ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L,
+    ngx_http_request_t *r);
+
+
+ngx_int_t
+ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf)
+{
+
+#if defined(LIBRESSL_VERSION_NUMBER)
+
+    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                  "LibreSSL does not support by proxy_ssl_verify_by_lua*");
+
+    return NGX_ERROR;
+
+#elif defined(OPENSSL_IS_BORINGSSL)
+
+    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                  "BoringSSL does not support by proxy_ssl_verify_by_lua*");
+
+    return NGX_ERROR;
+
+#else
+
+    void                      *plcf;
+    ngx_http_upstream_conf_t  *ucf;
+    ngx_ssl_t                 *ssl;
+
+    /*
+     * Nginx doesn't export ngx_http_proxy_loc_conf_t, so we can't directly
+     * get plcf here, but the first member of plcf is ngx_http_upstream_conf_t
+     */
+    plcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_module);
+    ucf = plcf;
+
+    ssl = ucf->ssl;
+
+    if (!ssl->ctx) {
+        ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "proxy_ssl_verify_by_lua* "
+                      "should be used with proxy_pass https url");
+
+        return NGX_ERROR;
+    }
+
+#if (!defined SSL_ERROR_WANT_RETRY_VERIFY                                    \
+     || OPENSSL_VERSION_NUMBER < 0x30000020L)
+
+    ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "OpenSSL too old to support "
+                  "proxy_ssl_verify_by_lua*");
+
+    return NGX_ERROR;
+
+#else
+
+    SSL_CTX_set_cert_verify_callback(ssl->ctx,
+                                     ngx_http_lua_proxy_ssl_verify_handler,
+                                     NULL);
+    return NGX_OK;
+
+#endif
+
+#endif
+}
+
+
+ngx_int_t
+ngx_http_lua_proxy_ssl_verify_handler_file(ngx_http_request_t *r,
+    ngx_http_lua_loc_conf_t *llcf, lua_State *L)
+{
+    ngx_int_t           rc;
+
+    rc = ngx_http_lua_cache_loadfile(r->connection->log, L,
+                                     llcf->proxy_ssl_verify_src.data,
+                                     &llcf->proxy_ssl_verify_src_ref,
+                                     llcf->proxy_ssl_verify_src_key);
+    if (rc != NGX_OK) {
+        return rc;
+    }
+
+    /*  make sure we have a valid code chunk */
+    ngx_http_lua_assert(lua_isfunction(L, -1));
+
+    return ngx_http_lua_proxy_ssl_verify_by_chunk(L, r);
+}
+
+
+ngx_int_t
+ngx_http_lua_proxy_ssl_verify_handler_inline(ngx_http_request_t *r,
+    ngx_http_lua_loc_conf_t *llcf, lua_State *L)
+{
+    ngx_int_t           rc;
+
+    rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
+                                       llcf->proxy_ssl_verify_src.data,
+                                       llcf->proxy_ssl_verify_src.len,
+                                       &llcf->proxy_ssl_verify_src_ref,
+                                       llcf->proxy_ssl_verify_src_key,
+                           (const char *) llcf->proxy_ssl_verify_chunkname);
+    if (rc != NGX_OK) {
+        return rc;
+    }
+
+    /*  make sure we have a valid code chunk */
+    ngx_http_lua_assert(lua_isfunction(L, -1));
+
+    return ngx_http_lua_proxy_ssl_verify_by_chunk(L, r);
+}
+
+
+char *
+ngx_http_lua_proxy_ssl_verify_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf)
+{
+    char        *rv;
+    ngx_conf_t   save;
+
+    save = *cf;
+    cf->handler = ngx_http_lua_proxy_ssl_verify_by_lua;
+    cf->handler_conf = conf;
+
+    rv = ngx_http_lua_conf_lua_block_parse(cf, cmd);
+
+    *cf = save;
+
+    return rv;
+}
+
+
+char *
+ngx_http_lua_proxy_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf)
+{
+#if defined(LIBRESSL_VERSION_NUMBER)
+
+    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                  "LibreSSL does not support by proxy_ssl_verify_by_lua*");
+
+    return NGX_CONF_ERROR;
+
+#elif defined(OPENSSL_IS_BORINGSSL)
+
+    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                  "BoringSSL does not support by proxy_ssl_verify_by_lua*");
+
+    return NGX_CONF_ERROR;
+
+#else
+
+#if (!defined SSL_ERROR_WANT_RETRY_VERIFY                                    \
+     || OPENSSL_VERSION_NUMBER < 0x30000020L)
+
+    /* SSL_set_retry_verify() was added in OpenSSL 3.0.2 */
+    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                  "at least OpenSSL 3.0.2 required but found "
+                  OPENSSL_VERSION_TEXT);
+
+    return NGX_CONF_ERROR;
+
+#else
+
+    size_t                       chunkname_len;
+    u_char                      *chunkname;
+    u_char                      *cache_key = NULL;
+    u_char                      *name;
+    ngx_str_t                   *value;
+    ngx_http_lua_loc_conf_t     *llcf = conf;
+
+    /*  must specify a concrete handler */
+    if (cmd->post == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    if (llcf->proxy_ssl_verify_handler) {
+        return "is duplicate";
+    }
+
+    if (ngx_http_lua_ssl_init(cf->log) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    value = cf->args->elts;
+
+    llcf->proxy_ssl_verify_handler =
+        (ngx_http_lua_loc_conf_handler_pt) cmd->post;
+
+    if (cmd->post == ngx_http_lua_proxy_ssl_verify_handler_file) {
+        /* Lua code in an external file */
+
+        name = ngx_http_lua_rebase_path(cf->pool, value[1].data,
+                                        value[1].len);
+        if (name == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        cache_key = ngx_http_lua_gen_file_cache_key(cf, value[1].data,
+                                                    value[1].len);
+        if (cache_key == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        llcf->proxy_ssl_verify_src.data = name;
+        llcf->proxy_ssl_verify_src.len = ngx_strlen(name);
+
+    } else {
+        cache_key = ngx_http_lua_gen_chunk_cache_key(cf,
+                                                     "proxy_ssl_verify_by_lua",
+                                                     value[1].data,
+                                                     value[1].len);
+        if (cache_key == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        chunkname = ngx_http_lua_gen_chunk_name(cf, "proxy_ssl_verify_by_lua",
+                                          sizeof("proxy_ssl_verify_by_lua") - 1,
+                                          &chunkname_len);
+        if (chunkname == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        /* Don't eval nginx variables for inline lua code */
+        llcf->proxy_ssl_verify_src = value[1];
+        llcf->proxy_ssl_verify_chunkname = chunkname;
+    }
+
+    llcf->proxy_ssl_verify_src_key = cache_key;
+
+    return NGX_CONF_OK;
+
+#endif  /* SSL_ERROR_WANT_RETRY_VERIFY */
+
+#endif
+}
+
+
+int
+ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store, void *arg)
+{
+#if defined(LIBRESSL_VERSION_NUMBER)
+    ngx_connection_t                *c;
+
+    c = ngx_ssl_get_connection(ssl_conn);  /* upstream connection */
+    ngx_ssl_conn_t                  *ssl_conn;
+
+    ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,
+                                          SSL_get_ex_data_X509_STORE_CTX_idx());
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+        "LibreSSL does not support by proxy_ssl_verify_by_lua*");
+
+    return 1;
+
+#elif defined(OPENSSL_IS_BORINGSSL)
+    ngx_connection_t                *c;
+    ngx_ssl_conn_t                  *ssl_conn;
+
+    ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,
+                                          SSL_get_ex_data_X509_STORE_CTX_idx());
+    c = ngx_ssl_get_connection(ssl_conn);  /* upstream connection */
+
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+        "BoringSSL does not support by proxy_ssl_verify_by_lua*");
+
+    return 1;
+#elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x30000020uL)
+
+    ngx_connection_t                *c;
+    ngx_ssl_conn_t                  *ssl_conn;
+
+    ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,
+                                          SSL_get_ex_data_X509_STORE_CTX_idx());
+    c = ngx_ssl_get_connection(ssl_conn);  /* upstream connection */
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+        "OpenSSL(< 3.0.2) does not support by proxy_ssl_verify_by_lua*");
+
+    return 1;
+
+#else
+
+    lua_State                       *L;
+    ngx_int_t                        rc;
+    ngx_connection_t                *c;
+    ngx_http_request_t              *r = NULL;
+    ngx_pool_cleanup_t              *cln;
+    ngx_http_lua_loc_conf_t         *llcf;
+    ngx_http_lua_ssl_ctx_t          *cctx;
+    ngx_ssl_conn_t                  *ssl_conn;
+
+    ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,
+                                          SSL_get_ex_data_X509_STORE_CTX_idx());
+
+    c = ngx_ssl_get_connection(ssl_conn);  /* upstream connection */
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "proxy ssl verify: connection reusable: %ud", c->reusable);
+
+    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
+
+    dd("proxy ssl verify handler, cert-verify-ctx=%p", cctx);
+
+    if (cctx && cctx->entered_proxy_ssl_verify_handler) {
+        /* not the first time */
+
+        if (cctx->done) {
+            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                           "proxy_ssl_verify_by_lua: "
+                           "cert verify callback exit code: %d",
+                           cctx->exit_code);
+
+            dd("lua proxy ssl verify done, finally");
+            return cctx->exit_code;
+        }
+
+        return SSL_set_retry_verify(ssl_conn);
+    }
+
+    dd("first time");
+
+#if (nginx_version < 1017009)
+    ngx_reusable_connection(c, 0);
+#endif
+
+    r = c->data;
+
+    if (cctx == NULL) {
+        cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t));
+        if (cctx == NULL) {
+            goto failed;  /* error */
+        }
+
+        cctx->ctx_ref = LUA_NOREF;
+    }
+
+    cctx->connection = c;
+    cctx->request = r;
+    cctx->x509_store = x509_store;
+    cctx->exit_code = 1;  /* successful by default */
+    cctx->original_request_count = r->main->count;
+    cctx->done = 0;
+    cctx->entered_proxy_ssl_verify_handler = 1;
+    cctx->pool = ngx_create_pool(128, c->log);
+    if (cctx->pool == NULL) {
+        goto failed;
+    }
+
+    dd("setting cctx");
+
+    if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index,
+                        cctx) == 0)
+    {
+        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");
+        goto failed;
+    }
+
+    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
+    if (llcf->upstream_skip_openssl_default_verify == 0) {
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "proxy_ssl_verify_by_lua: openssl default verify");
+
+        rc = X509_verify_cert(x509_store);
+        if (rc == 0) {
+            goto failed;
+        }
+    }
+
+    /* TODO honor lua_code_cache off */
+    L = ngx_http_lua_get_lua_vm(r, NULL);
+
+    c->log->action = "loading proxy ssl verify by lua";
+
+    rc = llcf->proxy_ssl_verify_handler(r, llcf, L);
+
+    if (rc >= NGX_OK || rc == NGX_ERROR) {
+        cctx->done = 1;
+
+        if (cctx->cleanup) {
+            *cctx->cleanup = NULL;
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "proxy_ssl_verify_by_lua: handler return value: %i, "
+                       "cert verify callback exit code: %d", rc,
+                       cctx->exit_code);
+
+        c->log->action = "proxy pass SSL handshaking";
+        return cctx->exit_code;
+    }
+
+    /* rc == NGX_DONE */
+
+    cln = ngx_pool_cleanup_add(cctx->pool, 0);
+    if (cln == NULL) {
+        goto failed;
+    }
+
+    cln->handler = ngx_http_lua_proxy_ssl_verify_done;
+    cln->data = cctx;
+
+    if (cctx->cleanup == NULL) {
+        cln = ngx_pool_cleanup_add(c->pool, 0);
+        if (cln == NULL) {
+            goto failed;
+        }
+
+        cln->data = cctx;
+        cctx->cleanup = &cln->handler;
+    }
+
+    *cctx->cleanup = ngx_http_lua_proxy_ssl_verify_aborted;
+
+    return SSL_set_retry_verify(ssl_conn);
+
+failed:
+
+    if (cctx && cctx->pool) {
+        ngx_destroy_pool(cctx->pool);
+    }
+
+    return 0;  /* verify failure or error */
+
+#endif
+}
+
+
+#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x30000020uL)
+static void
+ngx_http_lua_proxy_ssl_verify_done(void *data)
+{
+    ngx_connection_t        *c;
+    ngx_http_lua_ssl_ctx_t  *cctx = data;
+
+    dd("lua proxy ssl verify done");
+
+    if (cctx->aborted) {
+        return;
+    }
+
+    ngx_http_lua_assert(cctx->done == 0);
+
+    cctx->done = 1;
+
+    if (cctx->cleanup) {
+        *cctx->cleanup = NULL;
+    }
+
+    c = cctx->connection;
+
+    if (c->read->timer_set) {
+        ngx_del_timer(c->read);
+    }
+
+    if (c->write->timer_set) {
+        ngx_del_timer(c->write);
+    }
+
+    c->log->action = "proxy pass SSL handshaking";
+
+    ngx_post_event(c->write, &ngx_posted_events);
+}
+
+
+static void
+ngx_http_lua_proxy_ssl_verify_aborted(void *data)
+{
+    ngx_http_lua_ssl_ctx_t  *cctx = data;
+
+    dd("lua proxy ssl verify aborted");
+
+    if (cctx->done) {
+        /* completed successfully already */
+        return;
+    }
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,
+                   "proxy_ssl_verify_by_lua: cert verify callback aborted");
+
+    cctx->aborted = 1;
+    cctx->connection->ssl = NULL;
+    cctx->exit_code = 0;
+    if (cctx->pool) {
+        ngx_destroy_pool(cctx->pool);
+        cctx->pool = NULL;
+    }
+}
+#endif
+
+
+static ngx_int_t
+ngx_http_lua_proxy_ssl_verify_by_chunk(lua_State *L, ngx_http_request_t *r)
+{
+    int                      co_ref;
+    ngx_int_t                rc;
+    lua_State               *co;
+    ngx_http_lua_ctx_t      *ctx;
+    ngx_pool_cleanup_t      *cln;
+    ngx_http_upstream_t     *u;
+    ngx_connection_t        *c;
+    ngx_http_lua_ssl_ctx_t  *cctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
+
+    if (ctx == NULL) {
+        ctx = ngx_http_lua_create_ctx(r);
+        if (ctx == NULL) {
+            rc = NGX_ERROR;
+            ngx_http_lua_finalize_request(r, rc);
+            return rc;
+        }
+
+    } else {
+        dd("reset ctx");
+        ngx_http_lua_reset_ctx(r, L, ctx);
+    }
+
+    ctx->entered_content_phase = 1;
+
+    /*  {{{ new coroutine to handle request */
+    co = ngx_http_lua_new_thread(r, L, &co_ref);
+
+    if (co == NULL) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "lua: failed to create new coroutine to handle request");
+
+        rc = NGX_ERROR;
+        ngx_http_lua_finalize_request(r, rc);
+        return rc;
+    }
+
+    /*  move code closure to new coroutine */
+    lua_xmove(L, co, 1);
+
+#ifndef OPENRESTY_LUAJIT
+    /*  set closure's env table to new coroutine's globals table */
+    ngx_http_lua_get_globals_table(co);
+    lua_setfenv(co, -2);
+#endif
+
+    /* save nginx request in coroutine globals table */
+    ngx_http_lua_set_req(co, r);
+
+    ctx->cur_co_ctx = &ctx->entry_co_ctx;
+    ctx->cur_co_ctx->co = co;
+    ctx->cur_co_ctx->co_ref = co_ref;
+#ifdef NGX_LUA_USE_ASSERT
+    ctx->cur_co_ctx->co_top = 1;
+#endif
+
+    ngx_http_lua_attach_co_ctx_to_L(co, ctx->cur_co_ctx);
+
+    /* register request cleanup hooks */
+    if (ctx->cleanup == NULL) {
+        u = r->upstream;
+        c = u->peer.connection;
+        cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
+
+        cln = ngx_pool_cleanup_add(cctx->pool, 0);
+        if (cln == NULL) {
+            rc = NGX_ERROR;
+            ngx_http_lua_finalize_request(r, rc);
+            return rc;
+        }
+
+        cln->handler = ngx_http_lua_request_cleanup_handler;
+        cln->data = ctx;
+        ctx->cleanup = &cln->handler;
+    }
+
+    ctx->context = NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY;
+
+    rc = ngx_http_lua_run_thread(L, r, ctx, 0);
+
+    if (rc == NGX_ERROR || rc >= NGX_OK) {
+        /* do nothing */
+
+    } else if (rc == NGX_AGAIN) {
+        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);
+
+    } else if (rc == NGX_DONE) {
+        rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);
+
+    } else {
+        rc = NGX_OK;
+    }
+
+    ngx_http_lua_finalize_request(r, rc);
+    return rc;
+}
+
+
+/*
+ * openssl's doc of SSL_CTX_set_cert_verify_callback:
+ * In any case a viable verification result value must
+ * be reflected in the error member of x509_store_ctx,
+ * which can be done using X509_STORE_CTX_set_error.
+ */
+int
+ngx_http_lua_ffi_proxy_ssl_set_verify_result(ngx_http_request_t *r,
+    int verify_result, char **err)
+{
+#if defined(LIBRESSL_VERSION_NUMBER)
+
+    *err = "LibreSSL does not support this function";
+
+    return NGX_ERROR;
+
+#elif defined(OPENSSL_IS_BORINGSSL)
+
+    *err = "BoringSSL does not support this function";
+
+    return NGX_ERROR;
+
+#else
+
+#ifdef SSL_ERROR_WANT_RETRY_VERIFY
+    ngx_http_upstream_t             *u;
+    ngx_ssl_conn_t                  *ssl_conn;
+    ngx_connection_t                *c;
+    ngx_http_lua_ssl_ctx_t          *cctx;
+    X509_STORE_CTX                  *x509_store;
+
+    u = r->upstream;
+    if (u == NULL) {
+        *err = "bad request";
+        return NGX_ERROR;
+    }
+
+    c = u->peer.connection;
+    if (c == NULL || c->ssl == NULL) {
+        *err = "bad upstream connection";
+        return NGX_ERROR;
+    }
+
+    ssl_conn = c->ssl->connection;
+    if (ssl_conn == NULL) {
+        *err = "bad ssl conn";
+        return NGX_ERROR;
+    }
+
+    dd("get cctx session");
+
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
+    if (cctx == NULL) {
+        *err = "bad lua context";
+        return NGX_ERROR;
+    }
+
+    x509_store = cctx->x509_store;
+
+    X509_STORE_CTX_set_error(x509_store, verify_result);
+
+    return NGX_OK;
+#else
+    *err = "OpenSSL too old to support this function";
+
+    return NGX_ERROR;
+#endif
+
+#endif
+}
+
+
+int
+ngx_http_lua_ffi_proxy_ssl_get_verify_result(ngx_http_request_t *r, char **err)
+{
+#if defined(LIBRESSL_VERSION_NUMBER)
+
+    *err = "LibreSSL does not support this function";
+
+    return NGX_ERROR;
+
+#elif defined(OPENSSL_IS_BORINGSSL)
+
+    *err = "BoringSSL does not support this function";
+
+    return NGX_ERROR;
+
+#else
+
+#ifdef SSL_ERROR_WANT_RETRY_VERIFY
+    ngx_http_upstream_t             *u;
+    ngx_ssl_conn_t                  *ssl_conn;
+    ngx_connection_t                *c;
+    ngx_http_lua_ssl_ctx_t          *cctx;
+    X509_STORE_CTX                  *x509_store;
+
+    u = r->upstream;
+    if (u == NULL) {
+        *err = "bad request";
+        return NGX_ERROR;
+    }
+
+    c = u->peer.connection;
+    if (c == NULL || c->ssl == NULL) {
+        *err = "bad upstream connection";
+        return NGX_ERROR;
+    }
+
+    ssl_conn = c->ssl->connection;
+    if (ssl_conn == NULL) {
+        *err = "bad ssl conn";
+        return NGX_ERROR;
+    }
+
+    dd("get cctx session");
+
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
+    if (cctx == NULL) {
+        *err = "bad lua context";
+        return NGX_ERROR;
+    }
+
+    x509_store = cctx->x509_store;
+
+    return X509_STORE_CTX_get_error(x509_store);
+#else
+    *err = "OpenSSL too old to support this function";
+
+    return NGX_ERROR;
+#endif
+
+#endif
+}
+
+
+void
+ngx_http_lua_ffi_proxy_ssl_free_verify_cert(void *cdata)
+{
+    X509  *cert = cdata;
+
+    X509_free(cert);
+}
+
+
+void *
+ngx_http_lua_ffi_proxy_ssl_get_verify_cert(ngx_http_request_t *r, char **err)
+{
+#if defined(LIBRESSL_VERSION_NUMBER)
+
+    *err = "LibreSSL does not support this function";
+
+    return NGX_ERROR;
+
+#elif defined(OPENSSL_IS_BORINGSSL)
+
+    *err = "BoringSSL does not support this function";
+
+    return NGX_ERROR;
+
+#else
+
+#ifdef SSL_ERROR_WANT_RETRY_VERIFY
+    ngx_http_upstream_t             *u;
+    ngx_ssl_conn_t                  *ssl_conn;
+    ngx_connection_t                *c;
+    ngx_http_lua_ssl_ctx_t          *cctx;
+    X509_STORE_CTX                  *x509_store;
+    X509                            *x509;
+
+    u = r->upstream;
+    if (u == NULL) {
+        *err = "bad request";
+        return NULL;
+    }
+
+    c = u->peer.connection;
+    if (c == NULL || c->ssl == NULL) {
+        *err = "bad upstream connection";
+        return NULL;
+    }
+
+    ssl_conn = c->ssl->connection;
+    if (ssl_conn == NULL) {
+        *err = "bad ssl conn";
+        return NULL;
+    }
+
+    dd("get cctx session");
+
+    c = ngx_ssl_get_connection(ssl_conn);
+
+    cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
+    if (cctx == NULL) {
+        *err = "bad lua context";
+        return NULL;
+    }
+
+    x509_store = cctx->x509_store;
+
+    x509 = X509_STORE_CTX_get0_cert(x509_store);
+
+    if (!X509_up_ref(x509)) {
+        *err = "get verify result failed";
+        return NULL;
+    }
+
+    return x509;
+#else
+    *err = "OpenSSL too old to support this function";
+
+    return NULL;
+#endif
+
+#endif
+}
+
+
+#else  /* HAVE_PROXY_SSL_PATCH */
+
+
+int
+ngx_http_lua_ffi_proxy_ssl_set_verify_result(ngx_http_request_t *r,
+    int verify_result, char **err)
+{
+    *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function";
+
+    return NGX_ERROR;
+}
+
+
+int
+ngx_http_lua_ffi_proxy_ssl_get_verify_result(ngx_http_request_t *r, char **err)
+{
+    *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function";
+
+    return NGX_ERROR;
+}
+
+
+void
+ngx_http_lua_ffi_proxy_ssl_free_verify_cert(void *cdata)
+{
+}
+
+
+void *
+ngx_http_lua_ffi_proxy_ssl_get_verify_cert(ngx_http_request_t *r, char **err)
+{
+    *err = "Does not have HAVE_PROXY_SSL_PATCH to support this function";
+
+    return NULL;
+}
+
+#endif /* HAVE_PROXY_SSL_PATCH */
+#endif /* NGX_HTTP_SSL */
diff -pruN 1:0.10.28-3/src/ngx_http_lua_proxy_ssl_verifyby.h 1:0.10.29-1/src/ngx_http_lua_proxy_ssl_verifyby.h
--- 1:0.10.28-3/src/ngx_http_lua_proxy_ssl_verifyby.h	1970-01-01 00:00:00.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_proxy_ssl_verifyby.h	2025-12-07 15:22:42.000000000 +0000
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) Yichun Zhang (agentzh)
+ */
+
+#ifndef _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_
+#define _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_
+
+
+#include "ngx_http_lua_common.h"
+
+
+#if (NGX_HTTP_SSL)
+#ifdef HAVE_PROXY_SSL_PATCH
+
+/* do not introduce ngx_http_proxy_module to pollute ngx_http_lua_module.c */
+extern ngx_module_t  ngx_http_proxy_module;
+
+ngx_int_t ngx_http_lua_proxy_ssl_verify_handler_inline(ngx_http_request_t *r,
+    ngx_http_lua_loc_conf_t *llcf, lua_State *L);
+
+ngx_int_t ngx_http_lua_proxy_ssl_verify_handler_file(ngx_http_request_t *r,
+    ngx_http_lua_loc_conf_t *llcf, lua_State *L);
+
+char *ngx_http_lua_proxy_ssl_verify_by_lua_block(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
+
+char *ngx_http_lua_proxy_ssl_verify_by_lua(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
+int ngx_http_lua_proxy_ssl_verify_handler(X509_STORE_CTX *x509_store,
+    void *arg);
+
+ngx_int_t ngx_http_lua_proxy_ssl_verify_set_callback(ngx_conf_t *cf);
+
+#endif  /* HAVE_PROXY_SSL_PATCH */
+#endif  /* NGX_HTTP_SSL */
+
+
+#endif /* _NGX_HTTP_LUA_PROXY_SSL_VERIFYBY_H_INCLUDED_ */
+
+/* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff -pruN 1:0.10.28-3/src/ngx_http_lua_regex.c 1:0.10.29-1/src/ngx_http_lua_regex.c
--- 1:0.10.28-3/src/ngx_http_lua_regex.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_regex.c	2025-12-07 15:22:42.000000000 +0000
@@ -501,6 +501,11 @@ ngx_http_lua_regex_cleanup(void *data)
 
     if (ngx_regex_compile_context) {
         old_pool = ngx_http_lua_pcre_malloc_init(NULL);
+        if (ngx_regex_match_context != NULL) {
+            pcre2_match_context_free(ngx_regex_match_context);
+            ngx_regex_match_context = NULL;
+        }
+
         pcre2_compile_context_free(ngx_regex_compile_context);
         ngx_regex_compile_context = NULL;
         ngx_http_lua_pcre_malloc_done(old_pool);
diff -pruN 1:0.10.28-3/src/ngx_http_lua_rewriteby.c 1:0.10.29-1/src/ngx_http_lua_rewriteby.c
--- 1:0.10.28-3/src/ngx_http_lua_rewriteby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_rewriteby.c	2025-12-07 15:22:42.000000000 +0000
@@ -107,7 +107,9 @@ ngx_http_lua_rewrite_handler(ngx_http_re
         }
 
         if (rc == NGX_DECLINED) {
-            if (r->header_sent) {
+            if (r->header_sent
+                || (r->headers_out.status != 0 && ctx->out != NULL))
+            {
                 dd("header already sent");
 
                 /* response header was already generated in rewrite_by_lua*,
@@ -175,6 +177,10 @@ ngx_http_lua_rewrite_handler_inline(ngx_
     llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->rewrite_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua inline script (w/ cache) sp = 1 */
     rc = ngx_http_lua_cache_loadbuffer(r->connection->log, L,
                                        llcf->rewrite_src.value.data,
@@ -215,6 +221,10 @@ ngx_http_lua_rewrite_handler_file(ngx_ht
 
     L = ngx_http_lua_get_lua_vm(r, NULL);
 
+    if (!llcf->enable_code_cache) {
+        llcf->rewrite_src_ref = LUA_REFNIL;
+    }
+
     /*  load Lua script file (w/ cache)        sp = 1 */
     rc = ngx_http_lua_cache_loadfile(r->connection->log, L, script_path,
                                      &llcf->rewrite_src_ref,
@@ -349,7 +359,9 @@ ngx_http_lua_rewrite_by_chunk(lua_State
     }
 
     if (rc == NGX_OK || rc == NGX_DECLINED) {
-        if (r->header_sent) {
+        if (r->header_sent
+            || (r->headers_out.status != 0 && ctx->out != NULL))
+        {
             dd("header already sent");
 
             /* response header was already generated in rewrite_by_lua*,
diff -pruN 1:0.10.28-3/src/ngx_http_lua_server_rewriteby.c 1:0.10.29-1/src/ngx_http_lua_server_rewriteby.c
--- 1:0.10.28-3/src/ngx_http_lua_server_rewriteby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_server_rewriteby.c	2025-12-07 15:22:42.000000000 +0000
@@ -69,7 +69,9 @@ ngx_http_lua_server_rewrite_handler(ngx_
         }
 
         if (rc == NGX_DECLINED) {
-            if (r->header_sent) {
+            if (r->header_sent
+                || (r->headers_out.status != 0 && ctx->out != NULL))
+            {
                 dd("header already sent");
 
                 /* response header was already generated in rewrite_by_lua*,
@@ -307,7 +309,9 @@ ngx_http_lua_server_rewrite_by_chunk(lua
     }
 
     if (rc == NGX_OK || rc == NGX_DECLINED) {
-        if (r->header_sent) {
+        if (r->header_sent
+            || (r->headers_out.status != 0 && ctx->out != NULL))
+        {
             dd("header already sent");
 
             /* response header was already generated in rewrite_by_lua*,
diff -pruN 1:0.10.28-3/src/ngx_http_lua_socket_tcp.c 1:0.10.29-1/src/ngx_http_lua_socket_tcp.c
--- 1:0.10.28-3/src/ngx_http_lua_socket_tcp.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_socket_tcp.c	2025-12-07 15:22:42.000000000 +0000
@@ -128,7 +128,7 @@ static int ngx_http_lua_socket_tcp_conn_
     ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L);
 static int ngx_http_lua_socket_tcp_upstream_destroy(lua_State *L);
 static int ngx_http_lua_socket_downstream_destroy(lua_State *L);
-static ngx_int_t ngx_http_lua_socket_push_input_data(ngx_http_request_t *r,
+static void ngx_http_lua_socket_push_input_data(ngx_http_request_t *r,
     ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u,
     lua_State *L);
 static ngx_int_t ngx_http_lua_socket_add_pending_data(ngx_http_request_t *r,
@@ -227,6 +227,10 @@ static char ngx_http_lua_pattern_udata_m
 
 
 #define ngx_http_lua_tcp_socket_metatable_literal_key  "__tcp_cosocket_mt"
+#define ngx_http_lua_tcp_req_socket_metatable_literal_key                    \
+    "__tcp_req_cosocket_mt"
+#define ngx_http_lua_tcp_raw_req_socket_metatable_literal_key                \
+    "__tcp_raw_req_cosocket_mt"
 
 
 void
@@ -284,6 +288,12 @@ ngx_http_lua_inject_socket_tcp_api(ngx_l
     lua_setfield(L, -2, "__index");
 
     lua_rawset(L, LUA_REGISTRYINDEX);
+
+    lua_pushliteral(L, ngx_http_lua_tcp_req_socket_metatable_literal_key);
+    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
+                          req_socket_metatable_key));
+    lua_rawget(L, LUA_REGISTRYINDEX);
+    lua_rawset(L, LUA_REGISTRYINDEX);
     /* }}} */
 
     /* {{{raw req socket object metatable */
@@ -313,6 +323,12 @@ ngx_http_lua_inject_socket_tcp_api(ngx_l
     lua_setfield(L, -2, "__index");
 
     lua_rawset(L, LUA_REGISTRYINDEX);
+
+    lua_pushliteral(L, ngx_http_lua_tcp_raw_req_socket_metatable_literal_key);
+    lua_pushlightuserdata(L, ngx_http_lua_lightudata_mask(
+                          raw_req_socket_metatable_key));
+    lua_rawget(L, LUA_REGISTRYINDEX);
+    lua_rawset(L, LUA_REGISTRYINDEX);
     /* }}} */
 
     /* {{{tcp object metatable */
@@ -837,6 +853,7 @@ no_memory_and_not_resuming:
 static int
 ngx_http_lua_socket_tcp_bind(lua_State *L)
 {
+    int                   port;
     ngx_http_request_t   *r;
     ngx_http_lua_ctx_t   *ctx;
     int                   n;
@@ -846,8 +863,9 @@ ngx_http_lua_socket_tcp_bind(lua_State *
 
     n = lua_gettop(L);
 
-    if (n != 2) {
-        return luaL_error(L, "expecting 2 arguments, but got %d",
+    /* Correct the parameter check and allow 2 or 3 parameters */
+    if (n != 2 && n != 3) {
+        return luaL_error(L, "expecting 2 or 3 arguments, but got %d",
                           lua_gettop(L));
     }
 
@@ -865,6 +883,26 @@ ngx_http_lua_socket_tcp_bind(lua_State *
 
     luaL_checktype(L, 1, LUA_TTABLE);
 
+    port = 0;
+    /* handle case: host:port */
+    /* Hit the following parameter combination:
+     * sock:bind("127.0.0.1", port)     */
+    if (n == 3) {
+        if (!lua_isnumber(L, 3)) {
+            lua_pushnil(L);
+            lua_pushfstring(L, "expecting port to be a"
+                            "number but got type: %s", luaL_typename(L, 3));
+            return 2;
+        }
+
+        port = (int) lua_tointeger(L, 3);
+        if (port < 0 || port > 65535) {
+            lua_pushnil(L);
+            lua_pushfstring(L, "bad port number: %d", port);
+            return 2;
+        }
+    }
+
     text = (u_char *) luaL_checklstring(L, 2, &len);
 
     local = ngx_http_lua_parse_addr(L, text, len);
@@ -874,9 +912,11 @@ ngx_http_lua_socket_tcp_bind(lua_State *
         return 2;
     }
 
+    if (port > 0) {
+        ngx_inet_set_port(local->sockaddr, (in_port_t) port);
+    }
     /* TODO: we may reuse the userdata here */
     lua_rawseti(L, 1, SOCKET_BIND_INDEX);
-
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "lua tcp socket bind ip: %V", &local->name);
 
@@ -1129,6 +1169,12 @@ ngx_http_lua_socket_tcp_connect(lua_Stat
 
     lua_rawgeti(L, 1, SOCKET_BIND_INDEX);
     local = lua_touserdata(L, -1);
+
+    if (local != NULL) {
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "lua tcp socket sock:connect ip: %V", &local->name);
+    }
+
     lua_pop(L, 1);
 
     if (local) {
@@ -3153,7 +3199,6 @@ ngx_http_lua_socket_tcp_receive_retval_h
     ngx_http_lua_socket_tcp_upstream_t *u, lua_State *L)
 {
     int                          n;
-    ngx_int_t                    rc;
     ngx_http_lua_ctx_t          *ctx;
     ngx_event_t                 *ev;
 
@@ -3200,12 +3245,7 @@ ngx_http_lua_socket_tcp_receive_retval_h
         dd("u->bufs_in: %p", u->bufs_in);
 
         if (u->bufs_in) {
-            rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L);
-            if (rc == NGX_ERROR) {
-                lua_pushnil(L);
-                lua_pushliteral(L, "no memory");
-                return 2;
-            }
+            ngx_http_lua_socket_push_input_data(r, ctx, u, L);
 
             (void) ngx_http_lua_socket_read_error_retval_handler(r, u, L);
 
@@ -3219,12 +3259,7 @@ ngx_http_lua_socket_tcp_receive_retval_h
         return n + 1;
     }
 
-    rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L);
-    if (rc == NGX_ERROR) {
-        lua_pushnil(L);
-        lua_pushliteral(L, "no memory");
-        return 2;
-    }
+    ngx_http_lua_socket_push_input_data(r, ctx, u, L);
 
     return 1;
 }
@@ -3430,7 +3465,7 @@ ngx_http_lua_socket_tcp_handler(ngx_even
 static ngx_int_t
 ngx_http_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data)
 {
-    /* empty */
+    pc->type = SOCK_STREAM;
     return NGX_OK;
 }
 
@@ -5908,7 +5943,7 @@ ngx_http_lua_socket_downstream_destroy(l
 }
 
 
-static ngx_int_t
+static void
 ngx_http_lua_socket_push_input_data(ngx_http_request_t *r,
     ngx_http_lua_ctx_t *ctx, ngx_http_lua_socket_tcp_upstream_t *u,
     lua_State *L)
@@ -5980,8 +6015,6 @@ ngx_http_lua_socket_push_input_data(ngx_
         u->buf_in->buf->last = u->buffer.pos;
         u->buf_in->buf->pos = u->buffer.pos;
     }
-
-    return NGX_OK;
 }
 
 
@@ -6697,6 +6730,28 @@ ngx_http_lua_ffi_socket_tcp_setoption(ng
 }
 
 
+int
+ngx_http_lua_ffi_socket_tcp_getfd(ngx_http_request_t *r,
+    ngx_http_lua_socket_tcp_upstream_t *u, const char **errmsg)
+{
+    int fd;
+
+    *errmsg = NULL;
+
+    if (u == NULL  || u->peer.connection == NULL) {
+        *errmsg = "closed";
+        return -1;
+    }
+
+    fd = u->peer.connection->fd;
+    if (fd == -1) {
+        *errmsg = "faked connection";
+    }
+
+    return fd;
+}
+
+
 /* just hack the fd for testing bad case, it will also return the original fd */
 int
 ngx_http_lua_ffi_socket_tcp_hack_fd(ngx_http_lua_socket_tcp_upstream_t *u,
diff -pruN 1:0.10.28-3/src/ngx_http_lua_socket_udp.c 1:0.10.29-1/src/ngx_http_lua_socket_udp.c
--- 1:0.10.28-3/src/ngx_http_lua_socket_udp.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_socket_udp.c	2025-12-07 15:22:42.000000000 +0000
@@ -1207,7 +1207,7 @@ ngx_http_lua_socket_udp_read(ngx_http_re
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "lua udp socket read data: waiting: %d", (int) u->waiting);
 
-    n = ngx_udp_recv(u->udp_connection.connection,
+    n = ngx_udp_recv(c,
                      ngx_http_lua_socket_udp_buffer, u->recv_buf_size);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
@@ -1481,7 +1481,6 @@ ngx_http_lua_udp_connect(ngx_http_lua_ud
 #endif
 
     if (local != NULL) {
-        fprintf(stderr, "=== have local address\n");
         if (bind(s, local->sockaddr, local->socklen) == -1) {
             ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,
                           "bind(%V) failed", &local->name);
diff -pruN 1:0.10.28-3/src/ngx_http_lua_ssl.c 1:0.10.29-1/src/ngx_http_lua_ssl.c
--- 1:0.10.28-3/src/ngx_http_lua_ssl.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_ssl.c	2025-12-07 15:22:42.000000000 +0000
@@ -14,6 +14,7 @@
 
 
 int ngx_http_lua_ssl_ctx_index = -1;
+int ngx_http_lua_ssl_key_log_index = -1;
 
 
 ngx_int_t
@@ -29,6 +30,17 @@ ngx_http_lua_ssl_init(ngx_log_t *log)
             return NGX_ERROR;
         }
     }
+
+    if (ngx_http_lua_ssl_key_log_index == -1) {
+        ngx_http_lua_ssl_key_log_index = SSL_get_ex_new_index(0, NULL, NULL,
+                                                              NULL, NULL);
+
+        if (ngx_http_lua_ssl_key_log_index == -1) {
+            ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+                          "lua: SSL_get_ex_new_index() for key log failed");
+            return NGX_ERROR;
+        }
+    }
 
     return NGX_OK;
 }
diff -pruN 1:0.10.28-3/src/ngx_http_lua_ssl.h 1:0.10.29-1/src/ngx_http_lua_ssl.h
--- 1:0.10.28-3/src/ngx_http_lua_ssl.h	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_ssl.h	2025-12-07 15:22:42.000000000 +0000
@@ -16,7 +16,7 @@
 
 typedef struct {
     ngx_connection_t        *connection; /* original true connection */
-    ngx_http_request_t      *request;    /* fake request */
+    ngx_http_request_t      *request;
     ngx_pool_cleanup_pt     *cleanup;
 
     ngx_ssl_session_t       *session;    /* return value for openssl's
@@ -24,27 +24,48 @@ typedef struct {
 
     ngx_str_t                session_id;
 
+#ifdef HAVE_PROXY_SSL_PATCH
+    X509_STORE_CTX          *x509_store;
+    ngx_pool_t              *pool;
+#endif
+
     int                      exit_code;  /* exit code for openssl's
                                             set_client_hello_cb or
-                                            set_cert_cb callback */
+                                            set_cert_cb callback or
+                                            SSL_CTX_set_cert_verify_callback */
 
     int                      ctx_ref;  /*  reference to anchor
                                            request ctx data in lua
                                            registry */
 
+#ifdef HAVE_PROXY_SSL_PATCH
+    /* same size as count field of ngx_http_request_t */
+    unsigned                 original_request_count:16;
+#endif
     unsigned                 done:1;
     unsigned                 aborted:1;
 
     unsigned                 entered_client_hello_handler:1;
     unsigned                 entered_cert_handler:1;
     unsigned                 entered_sess_fetch_handler:1;
+#ifdef HAVE_PROXY_SSL_PATCH
+    unsigned                 entered_proxy_ssl_verify_handler:1;
+#endif
 } ngx_http_lua_ssl_ctx_t;
 
 
+typedef struct {
+    ngx_ssl_t     *ssl;
+    ngx_fd_t       fd;
+    ngx_str_t      name;
+} ngx_http_lua_ssl_key_log_t;
+
+
 ngx_int_t ngx_http_lua_ssl_init(ngx_log_t *log);
 
 
 extern int ngx_http_lua_ssl_ctx_index;
+extern int ngx_http_lua_ssl_key_log_index;
 
 
 #endif
diff -pruN 1:0.10.28-3/src/ngx_http_lua_ssl_certby.c 1:0.10.29-1/src/ngx_http_lua_ssl_certby.c
--- 1:0.10.28-3/src/ngx_http_lua_ssl_certby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_ssl_certby.c	2025-12-07 15:22:42.000000000 +0000
@@ -37,6 +37,10 @@ static u_char *ngx_http_lua_log_ssl_cert
 static ngx_int_t ngx_http_lua_ssl_cert_by_chunk(lua_State *L,
     ngx_http_request_t *r);
 
+#ifndef OPENSSL_IS_BORINGSSL
+static int ngx_http_lua_is_grease_cipher(uint16_t cipher_id);
+#endif
+
 
 ngx_int_t
 ngx_http_lua_ssl_cert_handler_file(ngx_http_request_t *r,
@@ -213,7 +217,7 @@ ngx_http_lua_ssl_cert_handler(ngx_ssl_co
 
         if (cctx->done) {
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                           "lua_certificate_by_lua: cert cb exit code: %d",
+                           "ssl_certificate_by_lua: cert cb exit code: %d",
                            cctx->exit_code);
 
             dd("lua ssl cert done, finally");
@@ -315,7 +319,7 @@ ngx_http_lua_ssl_cert_handler(ngx_ssl_co
         }
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "lua_certificate_by_lua: handler return value: %i, "
+                       "ssl_certificate_by_lua: handler return value: %i, "
                        "cert cb exit code: %d", rc, cctx->exit_code);
 
         c->log->action = "SSL handshaking";
@@ -346,7 +350,6 @@ ngx_http_lua_ssl_cert_handler(ngx_ssl_co
 
     return -1;
 
-#if 1
 failed:
 
     if (r && r->pool) {
@@ -358,15 +361,14 @@ failed:
     }
 
     return 0;
-#endif
 }
 
 
 static void
 ngx_http_lua_ssl_cert_done(void *data)
 {
-    ngx_connection_t                *c;
-    ngx_http_lua_ssl_ctx_t          *cctx = data;
+    ngx_connection_t        *c;
+    ngx_http_lua_ssl_ctx_t  *cctx = data;
 
     dd("lua ssl cert done");
 
@@ -387,6 +389,14 @@ ngx_http_lua_ssl_cert_done(void *data)
     c->log->action = "SSL handshaking";
 
     ngx_post_event(c->write, &ngx_posted_events);
+
+#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3)
+#   if OPENSSL_VERSION_NUMBER >= 0x1000205fL
+#       if (NGX_QUIC_OPENSSL_COMPAT || NGX_QUIC_OPENSSL_API)
+    ngx_http_lua_resume_quic_ssl_handshake(c);
+#       endif
+#   endif
+#endif
 }
 
 
@@ -395,7 +405,7 @@ ngx_http_lua_ssl_cert_aborted(void *data
 {
     ngx_http_lua_ssl_ctx_t      *cctx = data;
 
-    dd("lua ssl cert done");
+    dd("lua ssl cert aborted");
 
     if (cctx->done) {
         /* completed successfully already */
@@ -403,7 +413,7 @@ ngx_http_lua_ssl_cert_aborted(void *data
     }
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,
-                   "lua_certificate_by_lua: cert cb aborted");
+                   "ssl_certificate_by_lua: cert cb aborted");
 
     cctx->aborted = 1;
     cctx->request->connection->ssl = NULL;
@@ -446,6 +456,18 @@ ngx_http_lua_log_ssl_cert_error(ngx_log_
 }
 
 
+#ifndef OPENSSL_IS_BORINGSSL
+static int
+ngx_http_lua_is_grease_cipher(uint16_t cipher_id)
+{
+    /* GREASE values follow pattern: 0x?A?A where ? can be any hex digit */
+    /* and both ? must be the same */
+    /* Check if both bytes follow ?A pattern and high nibbles match */
+    return (cipher_id & 0x0F0F) == 0x0A0A;
+}
+#endif
+
+
 static ngx_int_t
 ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r)
 {
@@ -834,6 +856,70 @@ ngx_http_lua_ffi_ssl_raw_server_addr(ngx
 
 
 int
+ngx_http_lua_ffi_req_shared_ssl_ciphers(ngx_http_request_t *r,
+    uint16_t *ciphers, uint16_t *nciphers, int filter_grease, char **err)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+
+    *err = "BoringSSL is not supported for SSL cipher operations";
+    return NGX_ERROR;
+
+#else
+    ngx_ssl_conn_t         *ssl_conn;
+    STACK_OF(SSL_CIPHER)   *sk, *ck;
+    int                     sn, cn, i, n;
+    uint16_t                cipher;
+
+    if (r == NULL || r->connection == NULL || r->connection->ssl == NULL) {
+        *err = "bad request";
+        return NGX_ERROR;
+    }
+
+    ssl_conn = r->connection->ssl->connection;
+    if (ssl_conn == NULL) {
+        *err = "bad ssl conn";
+        return NGX_ERROR;
+    }
+
+    sk = SSL_get1_supported_ciphers(ssl_conn);
+    ck = SSL_get_client_ciphers(ssl_conn);
+    sn = sk_SSL_CIPHER_num(sk);
+    cn = sk_SSL_CIPHER_num(ck);
+
+    if (sn > *nciphers) {
+        *err = "buffer too small";
+        *nciphers = 0;
+        sk_SSL_CIPHER_free(sk);
+        return NGX_ERROR;
+    }
+
+    for (*nciphers = 0, i = 0; i < sn; i++) {
+        cipher = SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(sk, i));
+
+        /* Skip GREASE ciphers if filtering is enabled */
+        if (filter_grease && ngx_http_lua_is_grease_cipher(cipher)) {
+            continue;
+        }
+
+        for (n = 0; n < cn; n++) {
+            if (SSL_CIPHER_get_protocol_id(sk_SSL_CIPHER_value(ck, n))
+                == cipher)
+            {
+                ciphers[(*nciphers)++] = cipher;
+                break;
+            }
+        }
+    }
+
+    sk_SSL_CIPHER_free(sk);
+
+    return NGX_OK;
+#endif
+
+}
+
+
+int
 ngx_http_lua_ffi_ssl_server_name(ngx_http_request_t *r, char **name,
     size_t *namelen, char **err)
 {
@@ -1621,9 +1707,15 @@ failed:
 
 
 ngx_ssl_conn_t *
-ngx_http_lua_ffi_get_req_ssl_pointer(ngx_http_request_t *r)
+ngx_http_lua_ffi_get_req_ssl_pointer(ngx_http_request_t *r, const char **err)
 {
     if (r->connection == NULL || r->connection->ssl == NULL) {
+        *err = "bad request";
+        return NULL;
+    }
+
+    if (r->connection->ssl->connection == NULL) {
+        *err = "bad ssl connection";
         return NULL;
     }
 
diff -pruN 1:0.10.28-3/src/ngx_http_lua_ssl_client_helloby.c 1:0.10.29-1/src/ngx_http_lua_ssl_client_helloby.c
--- 1:0.10.28-3/src/ngx_http_lua_ssl_client_helloby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_ssl_client_helloby.c	2025-12-07 15:22:42.000000000 +0000
@@ -159,7 +159,7 @@ ngx_http_lua_ssl_client_hello_by_lua(ngx
         }
 
         chunkname = ngx_http_lua_gen_chunk_name(cf, "ssl_client_hello_by_lua",
-                                          sizeof("ssl_client_hello_by_lua")- 1,
+                                          sizeof("ssl_client_hello_by_lua") - 1,
                                           &chunkname_len);
         if (chunkname == NULL) {
             return NGX_CONF_ERROR;
@@ -207,7 +207,7 @@ ngx_http_lua_ssl_client_hello_handler(ng
 
         if (cctx->done) {
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                           "lua_client_hello_by_lua: "
+                           "ssl_client_hello_by_lua: "
                            "client hello cb exit code: %d",
                            cctx->exit_code);
 
@@ -220,6 +220,15 @@ ngx_http_lua_ssl_client_hello_handler(ng
 
     dd("first time");
 
+#if (nginx_version > 1029001)
+    /* see commit 0373fe5d98c1515640 for more details */
+    rc = ngx_ssl_client_hello_callback(ssl_conn, al, arg);
+
+    if (rc == 0) {
+        return rc;
+    }
+#endif
+
 #if (nginx_version < 1017009)
     ngx_reusable_connection(c, 0);
 #endif
@@ -310,7 +319,7 @@ ngx_http_lua_ssl_client_hello_handler(ng
         }
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
-                       "lua_client_hello_by_lua: handler return value: %i, "
+                       "ssl_client_hello_by_lua: handler return value: %i, "
                        "client hello cb exit code: %d", rc, cctx->exit_code);
 
         c->log->action = "SSL handshaking";
@@ -341,7 +350,6 @@ ngx_http_lua_ssl_client_hello_handler(ng
 
     return -1;
 
-#if 1
 failed:
 
     if (r && r->pool) {
@@ -353,15 +361,14 @@ failed:
     }
 
     return 0;
-#endif
 }
 
 
 static void
 ngx_http_lua_ssl_client_hello_done(void *data)
 {
-    ngx_connection_t                *c;
-    ngx_http_lua_ssl_ctx_t          *cctx = data;
+    ngx_connection_t        *c;
+    ngx_http_lua_ssl_ctx_t  *cctx = data;
 
     dd("lua ssl client hello done");
 
@@ -382,6 +389,14 @@ ngx_http_lua_ssl_client_hello_done(void
     c->log->action = "SSL handshaking";
 
     ngx_post_event(c->write, &ngx_posted_events);
+
+#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3)
+#   if defined(SSL_ERROR_WANT_CLIENT_HELLO_CB)
+#       if (NGX_QUIC_OPENSSL_COMPAT || NGX_QUIC_OPENSSL_API)
+    ngx_http_lua_resume_quic_ssl_handshake(c);
+#       endif
+#   endif
+#endif
 }
 
 
@@ -398,7 +413,7 @@ ngx_http_lua_ssl_client_hello_aborted(vo
     }
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cctx->connection->log, 0,
-                   "lua_client_hello_by_lua: client hello cb aborted");
+                   "ssl_client_hello_by_lua: client hello cb aborted");
 
     cctx->aborted = 1;
     cctx->request->connection->ssl = NULL;
@@ -662,6 +677,99 @@ ngx_http_lua_ffi_ssl_get_client_hello_ex
 }
 
 
+int
+ngx_http_lua_ffi_ssl_get_client_hello_ext_present(ngx_http_request_t *r,
+    int **extensions, size_t *extensions_len, char **err)
+{
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+    ngx_ssl_conn_t          *ssl_conn;
+    int                      got_extensions;
+    size_t                   ext_len;
+    int                     *ext_out;
+    /* OPENSSL will allocate memory for us and make the ext_out point to it */
+
+
+    if (r->connection == NULL || r->connection->ssl == NULL) {
+        *err = "bad request";
+        return NGX_ERROR;
+    }
+
+    ssl_conn = r->connection->ssl->connection;
+    if (ssl_conn == NULL) {
+        *err = "bad ssl conn";
+        return NGX_ERROR;
+    }
+
+    got_extensions = SSL_client_hello_get1_extensions_present(ssl_conn,
+                                                           &ext_out, &ext_len);
+    if (!got_extensions || !ext_out || !ext_len) {
+        *err = "failed SSL_client_hello_get1_extensions_present()";
+        return NGX_DECLINED;
+    }
+
+    *extensions = ngx_palloc(r->connection->pool, sizeof(int) * ext_len);
+    if (*extensions != NULL) {
+        ngx_memcpy(*extensions, ext_out, sizeof(int) * ext_len);
+        *extensions_len = ext_len;
+    }
+
+    OPENSSL_free(ext_out);
+    return NGX_OK;
+#else
+    *err = "OpenSSL too old to support this function";
+    return NGX_ERROR;
+#endif
+}
+
+
+int ngx_http_lua_ffi_ssl_get_client_hello_ciphers(ngx_http_request_t *r,
+    unsigned short *ciphers,  size_t ciphers_size, char **err)
+{
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+    int                      i;
+    size_t                   ciphers_cnt;
+    size_t                   ciphersuites_bytes;
+    ngx_ssl_conn_t          *ssl_conn;
+    const unsigned char     *ciphers_raw;
+
+    if (r->connection == NULL || r->connection->ssl == NULL) {
+        *err = "bad request";
+        return NGX_ERROR;
+    }
+
+    ssl_conn = r->connection->ssl->connection;
+    if (ssl_conn == NULL) {
+        *err = "bad ssl conn";
+        return NGX_ERROR;
+    }
+
+    ciphersuites_bytes = SSL_client_hello_get0_ciphers(ssl_conn, &ciphers_raw);
+
+    if (ciphersuites_bytes == 0) {
+        *err = "failed SSL_client_hello_get0_ciphers()";
+        return NGX_DECLINED;
+    }
+
+    if (ciphersuites_bytes % 2 != 0) {
+        *err = "SSL_client_hello_get0_ciphers() odd ciphersuites_bytes";
+        return NGX_DECLINED;
+    }
+
+    ciphers_cnt = ciphersuites_bytes / 2;
+    ciphers_cnt = ciphers_cnt > ciphers_size ? ciphers_size : ciphers_cnt;
+
+    for (i = 0 ; i < (int) ciphers_cnt ; i++) {
+        ciphers[i] = (ciphers_raw[i * 2] << 8) | ciphers_raw[i * 2 + 1];
+    }
+
+    return ciphers_cnt;
+#else
+    *err = "OpenSSL too old to support this function";
+    return NGX_ERROR;
+#endif
+}
+
+
 int
 ngx_http_lua_ffi_ssl_set_protocols(ngx_http_request_t *r,
     int protocols, char **err)
diff -pruN 1:0.10.28-3/src/ngx_http_lua_ssl_session_fetchby.c 1:0.10.29-1/src/ngx_http_lua_ssl_session_fetchby.c
--- 1:0.10.28-3/src/ngx_http_lua_ssl_session_fetchby.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_ssl_session_fetchby.c	2025-12-07 15:22:42.000000000 +0000
@@ -416,7 +416,7 @@ ngx_http_lua_ssl_sess_fetch_aborted(void
 {
     ngx_http_lua_ssl_ctx_t          *cctx = data;
 
-    dd("lua ssl sess_fetch done");
+    dd("lua ssl sess_fetch aborted");
 
     if (cctx->done) {
         /* completed successfully already */
diff -pruN 1:0.10.28-3/src/ngx_http_lua_util.c 1:0.10.29-1/src/ngx_http_lua_util.c
--- 1:0.10.28-3/src/ngx_http_lua_util.c	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_util.c	2025-12-07 15:22:42.000000000 +0000
@@ -49,6 +49,10 @@
 #if (NGX_THREADS)
 #include "ngx_http_lua_worker_thread.h"
 #endif
+#if (NGX_HTTP_V3)
+#include <ngx_event_quic.h>
+#include <ngx_event_quic_connection.h>
+#endif
 
 
 #if 1
@@ -1679,6 +1683,12 @@ no_parent:
 
 done:
 
+#ifdef HAVE_PROXY_SSL_PATCH
+    if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) {
+        return NGX_OK;
+    }
+#endif
+
     if (ctx->entered_content_phase
         && r->connection->fd != (ngx_socket_t) -1)
     {
@@ -2434,6 +2444,12 @@ ngx_http_lua_handle_exit(lua_State *L, n
         return ctx->exit_code;
     }
 
+#ifdef HAVE_PROXY_SSL_PATCH
+    if (ctx->context == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY) {
+        return ctx->exit_code;
+    }
+#endif
+
 #if 1
     if (!r->header_sent
         && !ctx->header_sent
@@ -3669,12 +3685,46 @@ void
 ngx_http_lua_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
 {
     ngx_http_lua_ctx_t              *ctx;
+#if (NGX_HTTP_SSL)
+#ifdef HAVE_PROXY_SSL_PATCH
+    ngx_http_upstream_t             *u;
+    ngx_connection_t                *c;
+    ngx_http_lua_ssl_ctx_t          *cctx;
+#endif
+#endif
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
     if (ctx && ctx->cur_co_ctx) {
         ngx_http_lua_cleanup_pending_operation(ctx->cur_co_ctx);
     }
 
+#if (NGX_HTTP_SSL)
+#ifdef HAVE_PROXY_SSL_PATCH
+    u = r->upstream;
+    if (u) {
+        c = u->peer.connection;
+        if (c && c->ssl) {
+            cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection);
+            if (cctx && cctx->pool) {
+                if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+                    cctx->exit_code = 0;
+                }
+
+                if (r->main->count > cctx->original_request_count) {
+                    r->main->count--;
+                    return;
+                }
+
+                ngx_destroy_pool(cctx->pool);
+                cctx->pool = NULL;
+
+                return;
+            }
+        }
+    }
+#endif
+#endif
+
     if (r->connection->fd != (ngx_socket_t) -1) {
         ngx_http_finalize_request(r, rc);
         return;
@@ -3826,6 +3876,18 @@ ngx_http_lua_close_fake_connection(ngx_c
     c->read->closed = 1;
     c->write->closed = 1;
 
+    /* When destroying the pool, the registered clean callbacks will be
+     * executed. If the ngx_connection_t is freed before these callbacks are
+     * run, and a new ngx_connection_t is created within a clean callback,
+     * it is possible for the freed ngx_connection_t to be reused again.
+     * If this reused ngx_connection_t is destroyed again within the clean
+     * callback logic, it may result in other clean callbacks holding a
+     * ngx_connection_t that has already been destroyed.
+     */
+    if (pool) {
+        ngx_destroy_pool(pool);
+    }
+
     /* we temporarily use a valid fd (0) to make ngx_free_connection happy */
 
     c->fd = 0;
@@ -3841,10 +3903,6 @@ ngx_http_lua_close_fake_connection(ngx_c
     if (ngx_cycle->files) {
         ngx_cycle->files[0] = saved_c;
     }
-
-    if (pool) {
-        ngx_destroy_pool(pool);
-    }
 }
 
 
@@ -4555,4 +4613,69 @@ ngx_http_lua_parse_addr(lua_State *L, u_
 }
 
 
+void
+ngx_http_lua_ffi_bypass_if_checks(ngx_http_request_t *r)
+{
+    r->disable_not_modified = 1;
+}
+
+
+#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3)
+void
+ngx_http_lua_resume_quic_ssl_handshake(ngx_connection_t *c)
+{
+    ngx_int_t        rc, sslerr;
+    ngx_ssl_conn_t  *ssl_conn;
+
+    if (c == NULL || c->ssl == NULL || c->ssl->connection == NULL) {
+        return;
+    }
+
+    if (!c->udp || c->read == NULL
+        || c->read->handler != ngx_quic_input_handler)
+    {
+        return;
+    }
+
+    ssl_conn = c->ssl->connection;
+
+    /* Openresty ssl lua scripts are triggered by registering callbacks into
+     * openssl. If a lua script calls a yield api during execution, openssl's
+     * SSL_do_handshake will return a specific error code. The application
+     * should not treat these codes as fatal. The lua script will resume on
+     * yield-related events until it finishes. After completion,
+     * SSL_do_handshake should be called again to advance openssl's state
+     * machine.
+     *
+     * Note that nginx quic and openresty ssl lua scripts are independent. When
+     * openresty ssl lua runs, the client is waiting for a server response, so
+     * nginx quic does not affect the execution of the lua script. After the lua
+     * script finishes, SSL_do_handshake is called again, and nginx quic's
+     * registered callback continues the handshake.
+     */
+    rc = SSL_do_handshake(ssl_conn);
+    sslerr = SSL_get_error(ssl_conn, rc);
+
+    if (rc <= 0 && sslerr != SSL_ERROR_WANT_READ
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+        && sslerr != SSL_ERROR_WANT_X509_LOOKUP
+#endif
+#ifdef SSL_ERROR_WANT_CLIENT_HELLO_CB
+        && sslerr != SSL_ERROR_WANT_CLIENT_HELLO_CB
+#endif
+    ) {
+        /* If a fatal error occurs or lua script exits with error during quic
+         * handshake, the quic connection will be closed immediately.
+         */
+        ngx_quic_close_connection(c, NGX_ERROR);
+
+    } else {
+        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "lua resuming quic ssl handshake: rc %d, err %d, will "
+                       "continue driving handshake or next lua script phase",
+                       rc, sslerr);
+    }
+}
+#endif
+
 /* vi:set ft=c ts=4 sw=4 et fdm=marker: */
diff -pruN 1:0.10.28-3/src/ngx_http_lua_util.h 1:0.10.29-1/src/ngx_http_lua_util.h
--- 1:0.10.28-3/src/ngx_http_lua_util.h	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/src/ngx_http_lua_util.h	2025-12-07 15:22:42.000000000 +0000
@@ -32,6 +32,20 @@
 
 #define NGX_HTTP_LUA_ESCAPE_HEADER_VALUE  8
 
+#ifdef HAVE_PROXY_SSL_PATCH
+
+#define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE         \
+                                | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE        \
+                                | NGX_HTTP_LUA_CONTEXT_ACCESS                \
+                                | NGX_HTTP_LUA_CONTEXT_CONTENT               \
+                                | NGX_HTTP_LUA_CONTEXT_TIMER                 \
+                                | NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY      \
+                                | NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO      \
+                                | NGX_HTTP_LUA_CONTEXT_SSL_CERT              \
+                                | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)
+
+#else
+
 #define NGX_HTTP_LUA_CONTEXT_YIELDABLE (NGX_HTTP_LUA_CONTEXT_REWRITE         \
                                 | NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE        \
                                 | NGX_HTTP_LUA_CONTEXT_ACCESS                \
@@ -41,11 +55,15 @@
                                 | NGX_HTTP_LUA_CONTEXT_SSL_CERT              \
                                 | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)
 
+#endif  /* HAVE_PROXY_SSL_PATCH */
+
 
 /* key in Lua vm registry for all the "ngx.ctx" tables */
 #define ngx_http_lua_ctx_tables_key  "ngx_lua_ctx_tables"
 
 
+#ifdef HAVE_PROXY_SSL_PATCH
+
 #define ngx_http_lua_context_name(c)                                         \
     ((c) == NGX_HTTP_LUA_CONTEXT_SET ? "set_by_lua*"                         \
      : (c) == NGX_HTTP_LUA_CONTEXT_REWRITE ? "rewrite_by_lua*"               \
@@ -59,6 +77,8 @@
      : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*"       \
      : (c) == NGX_HTTP_LUA_CONTEXT_EXIT_WORKER ? "exit_worker_by_lua*"       \
      : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? "balancer_by_lua*"             \
+     : (c) == NGX_HTTP_LUA_CONTEXT_PROXY_SSL_VERIFY ?                        \
+                                                 "proxy_ssl_verify_by_lua*"  \
      : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ?                        \
                                                  "ssl_client_hello_by_lua*"  \
      : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*"      \
@@ -68,6 +88,32 @@
                                                  "ssl_session_fetch_by_lua*" \
      : "(unknown)")
 
+#else
+
+#define ngx_http_lua_context_name(c)                                         \
+    ((c) == NGX_HTTP_LUA_CONTEXT_SET ? "set_by_lua*"                         \
+     : (c) == NGX_HTTP_LUA_CONTEXT_REWRITE ? "rewrite_by_lua*"               \
+     : (c) == NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE ? "server_rewrite_by_lua*" \
+     : (c) == NGX_HTTP_LUA_CONTEXT_ACCESS ? "access_by_lua*"                 \
+     : (c) == NGX_HTTP_LUA_CONTEXT_CONTENT ? "content_by_lua*"               \
+     : (c) == NGX_HTTP_LUA_CONTEXT_LOG ? "log_by_lua*"                       \
+     : (c) == NGX_HTTP_LUA_CONTEXT_HEADER_FILTER ? "header_filter_by_lua*"   \
+     : (c) == NGX_HTTP_LUA_CONTEXT_BODY_FILTER ? "body_filter_by_lua*"       \
+     : (c) == NGX_HTTP_LUA_CONTEXT_TIMER ? "ngx.timer"                       \
+     : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*"       \
+     : (c) == NGX_HTTP_LUA_CONTEXT_EXIT_WORKER ? "exit_worker_by_lua*"       \
+     : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? "balancer_by_lua*"             \
+     : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO ?                        \
+                                                 "ssl_client_hello_by_lua*"  \
+     : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*"      \
+     : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE ?                          \
+                                                 "ssl_session_store_by_lua*" \
+     : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH ?                          \
+                                                 "ssl_session_fetch_by_lua*" \
+     : "(unknown)")
+
+#endif  /* HAVE_PROXY_SSL_PATCH */
+
 
 #define ngx_http_lua_check_context(L, ctx, flags)                            \
     if (!((ctx)->context & (flags))) {                                       \
@@ -267,6 +313,10 @@ ngx_addr_t *ngx_http_lua_parse_addr(lua_
 
 size_t ngx_http_lua_escape_log(u_char *dst, u_char *src, size_t size);
 
+#if (HAVE_QUIC_SSL_LUA_YIELD_PATCH && NGX_HTTP_V3)
+void ngx_http_lua_resume_quic_ssl_handshake(ngx_connection_t *c);
+#endif
+
 
 static ngx_inline void
 ngx_http_lua_init_ctx(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)
diff -pruN 1:0.10.28-3/t/002-content.t 1:0.10.29-1/t/002-content.t
--- 1:0.10.28-3/t/002-content.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/002-content.t	2025-12-07 15:22:42.000000000 +0000
@@ -1120,3 +1120,4 @@ If-Unmodified-Since: Wed, 01 Jan 2020 07
 --- error_code: 412
 --- no_error_log
 unknown phase: 0
+--- skip_eval: 2:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
diff -pruN 1:0.10.28-3/t/005-exit.t 1:0.10.29-1/t/005-exit.t
--- 1:0.10.28-3/t/005-exit.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/005-exit.t	2025-12-07 15:22:42.000000000 +0000
@@ -659,6 +659,7 @@ GET /t
 --- response_body_like: 403 Forbidden
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
@@ -677,6 +678,7 @@ GET /t
 --- response_body
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
@@ -713,6 +715,7 @@ F(ngx_http_lua_header_filter_by_chunk).r
 --- no_error_log
 [error]
 [alert]
+--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
@@ -730,6 +733,7 @@ GET /t
 --- response_body
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
diff -pruN 1:0.10.28-3/t/014-bugs.t 1:0.10.29-1/t/014-bugs.t
--- 1:0.10.28-3/t/014-bugs.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/014-bugs.t	2025-12-07 15:22:42.000000000 +0000
@@ -9,7 +9,7 @@ log_level('debug');
 repeat_each(3);
 
 # NB: the shutdown_error_log block is independent from repeat times
-plan tests => repeat_each() * (blocks() * 2 + 33) + 1;
+plan tests => repeat_each() * (blocks() * 2 + 41);
 
 our $HtmlDir = html_dir;
 #warn $html_dir;
@@ -1269,14 +1269,16 @@ qr/\[emerg\] \d+#\d+: unexpected "A" in/
 
 
 === TEST 47: cosocket does not exit on worker_shutdown_timeout
+This test must enable master process
+--- SKIP
 --- main_config
 worker_shutdown_timeout 1;
 --- config
 location /t {
     content_by_lua_block {
-        local function thread_func()
+        local function thread_func(port)
             local sock = ngx.socket.tcp()
-            local ok, err = sock:connect("127.0.0.1", 65110)
+            local ok, err = sock:connect("127.0.0.1", port)
             local bytes, err = sock:send("hello")
             if bytes ~= 5 then
                 sock:close()
@@ -1293,11 +1295,11 @@ location /t {
             ngx.log(ngx.ERR, "successfully read a line: ", line)
         end
 
-        local function timer_func()
-            ngx.thread.spawn(thread_func)
+        local function timer_func(port)
+            ngx.thread.spawn(thread_func, port)
         end
 
-        ngx.timer.at(1, timer_func)
+        ngx.timer.at(1, timer_func, ngx.var.server_port)
         ngx.say("Hello world")
     }
 }
@@ -1311,12 +1313,11 @@ my $expr;
 if ($ENV{TEST_NGINX_USE_HTTP3}) {
     $expr = qr|lua close the global Lua VM|
 } else {
-    $expr = qr|failed to read a line: closed|
+    $expr = qr/failed to read a line: closed|attempt to send data on a closed socket/
 }
 
 $expr;
 --- timeout: 1.2
---- skip_eval: 2:$ENV{TEST_NGINX_USE_HTTP3}
 
 
 
@@ -1366,3 +1367,91 @@ If-Match: 1
 --- error_code: 412
 --- response_body eval
 qr/\Ahello\z/
+
+
+
+=== TEST 50: nginx crashes when encountering an illegal http if header
+crash with ngx.print()
+--- main_config
+--- config
+error_page 412 /my_error_handler_412;
+
+location /t {
+    access_by_lua_block {
+        local ngx_resp = require "ngx.resp"
+        ngx_resp.bypass_if_checks()
+        ngx.print("hello")
+        ngx.exit(200)
+    }
+}
+location = /my_error_handler_412 {
+    content_by_lua_block {
+        ngx.sleep(0.002)
+        ngx.header["Content-Type"] = "text/plain"
+    }
+}
+--- request
+    GET /t
+--- more_headers
+If-Match: 1
+--- error_code: 200
+--- response_body eval
+qr/\Ahello\z/
+
+
+
+=== TEST 51: subrequest cycle problem in rewrite_by_lua_file
+--- http_config
+    lua_code_cache off;
+--- config
+    set $main "foo";
+    set $sub "bar";
+    location = /main {
+        rewrite_by_lua_file html/main.lua;
+        echo $main;
+    }
+
+    location = /sub {
+        rewrite_by_lua_file html/sub.lua;
+        echo $sub;
+    }
+--- user_files
+>>> main.lua
+local res = ngx.location.capture("/sub")
+ngx.var.main = "main " .. res.body
+>>> sub.lua
+ngx.var.sub = "sub"
+
+--- pipelined_requests eval
+["GET /sub", "GET /main"]
+--- response_body eval
+["sub\n", "main sub\n\n"]
+--- no_error_log
+[error]
+
+
+
+=== TEST 52: subrequest cycle problem in content_by_lua_file
+--- http_config
+    lua_code_cache off;
+--- config
+    location = /main {
+        content_by_lua_file html/main.lua;
+    }
+
+    location = /sub {
+        content_by_lua_file html/sub.lua;
+    }
+--- user_files
+>>> main.lua
+local res = ngx.location.capture("/sub")
+ngx.print("main " .. res.body)
+>>> sub.lua
+ngx.print("sub")
+
+--- pipelined_requests eval
+["GET /sub", "GET /main"]
+--- response_body eval
+["sub", "main sub"]
+--- no_error_log
+[error]
diff -pruN 1:0.10.28-3/t/015-status.t 1:0.10.29-1/t/015-status.t
--- 1:0.10.28-3/t/015-status.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/015-status.t	2025-12-07 15:22:42.000000000 +0000
@@ -9,7 +9,7 @@ log_level('warn');
 #repeat_each(120);
 repeat_each(2);
 
-plan tests => repeat_each() * (blocks() * 2 + 10);
+plan tests => repeat_each() * (blocks() * 3 + 2);
 
 #no_diff();
 #no_long_string();
@@ -29,6 +29,8 @@ __DATA__
 GET /nil
 --- response_body
 nil
+--- no_error_log
+[error]
 
 
 
@@ -43,6 +45,8 @@ nil
 GET /nil
 --- response_body
 not nil
+--- no_error_log
+[error]
 
 
 
@@ -57,6 +61,8 @@ not nil
 GET /nil
 --- response_body
 0
+--- no_error_log
+[error]
 
 
 
@@ -73,6 +79,8 @@ GET /nil
 --- response_body
 blah
 200
+--- no_error_log
+[error]
 
 
 
@@ -89,6 +97,8 @@ GET /201
 --- response_body
 created
 --- error_code: 201
+--- no_error_log
+[error]
 
 
 
@@ -105,6 +115,8 @@ GET /201
 --- response_body
 created
 --- error_code: 201
+--- no_error_log
+[error]
 
 
 
@@ -121,6 +133,8 @@ GET /201
 --- response_body
 created
 --- error_code: 201
+--- no_error_log
+[error]
 
 
 
@@ -136,6 +150,8 @@ created
 GET /201
 --- response_body_like: 500 Internal Server Error
 --- error_code: 500
+--- no_error_log
+[crit]
 
 
 
@@ -248,6 +264,8 @@ GET /t
 --- http09
 --- response_body
 status = 9
+--- no_error_log
+[error]
 
 
 
@@ -345,3 +363,169 @@ GET /t
 match
 --- no_error_log
 [error]
+
+
+
+=== TEST 18: set ngx.status in server_rewrite_by_lua_block
+don't proxy_pass to upstream
+--- config
+    server_rewrite_by_lua_block {
+        if ngx.var.uri == "/t" then
+            ngx.status = 403
+            ngx.say("Hello World")
+        end
+    }
+
+    location /t {
+        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;
+    }
+
+    location /u {
+        content_by_lua_block {
+            ngx.say("From upstream")
+        }
+    }
+--- request
+GET /t HTTP/1.0
+--- response_body
+Hello World
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 19: set ngx.status in rewrite_by_lua_block
+don't proxy_pass to upstream
+--- config
+    location /t {
+        rewrite_by_lua_block {
+            ngx.status = 403
+            ngx.say("Hello World")
+        }
+        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;
+    }
+
+    location /u {
+        content_by_lua_block {
+            ngx.say("From upstream")
+        }
+    }
+--- request
+GET /t HTTP/1.0
+--- response_body
+Hello World
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 20: set ngx.status in access_by_lua_block
+don't proxy_pass to upstream
+--- config
+    location /t {
+        access_by_lua_block {
+            ngx.status = 403
+            ngx.say("Hello World")
+        }
+        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;
+    }
+
+    location /u {
+        content_by_lua_block {
+            ngx.say("From upstream")
+        }
+    }
+--- request
+GET /t HTTP/1.0
+--- response_body
+Hello World
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 21: set ngx.status in server_rewrite_by_lua_block with sleep
+don't proxy_pass to upstream
+--- config
+    server_rewrite_by_lua_block {
+        if ngx.var.uri == "/t" then
+            ngx.sleep(0.001)
+            ngx.status = 403
+            ngx.say("Hello World")
+        end
+    }
+
+    location /t {
+        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;
+    }
+
+    location /u {
+        content_by_lua_block {
+            ngx.say("From upstream")
+        }
+    }
+--- request
+GET /t HTTP/1.0
+--- response_body
+Hello World
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 22: set ngx.status in rewrite_by_lua_block with sleep
+don't proxy_pass to upstream
+--- config
+    location /t {
+        rewrite_by_lua_block {
+            ngx.sleep(0.001)
+            ngx.status = 403
+            ngx.say("Hello World")
+        }
+
+        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;
+    }
+
+    location /u {
+        content_by_lua_block {
+            ngx.say("From upstream")
+        }
+    }
+--- request
+GET /t HTTP/1.0
+--- response_body
+Hello World
+--- error_code: 403
+--- no_error_log
+[error]
+
+
+
+=== TEST 23: set ngx.status in access_by_lua_block
+don't proxy_pass to upstream
+--- config
+    location /t {
+        access_by_lua_block {
+            ngx.sleep(0.001)
+            ngx.status = 403
+            ngx.say("Hello World")
+        }
+        proxy_pass http://127.0.0.1:$TEST_NGINX_SERVER_PORT/u;
+    }
+
+    location /u {
+        content_by_lua_block {
+            ngx.say("From upstream")
+        }
+    }
+--- request
+GET /t HTTP/1.0
+--- response_body
+Hello World
+--- error_code: 403
+--- no_error_log
+[error]
diff -pruN 1:0.10.28-3/t/016-resp-header.t 1:0.10.29-1/t/016-resp-header.t
--- 1:0.10.28-3/t/016-resp-header.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/016-resp-header.t	2025-12-07 15:22:42.000000000 +0000
@@ -2183,9 +2183,9 @@ foo: foo%0Axx:bar\r\nfoo: bar%0Dxxx:foo\
 hi
 --- no_error_log
 [alert]
---- error_log
-my Content-Length: 8589934591
-upstream prematurely closed connection while sending to client
+--- error_log eval
+[ "my Content-Length: 8589934591",
+qr/upstream prematurely closed connection while sending to client|upstream prematurely closed connection while reading upstream/]
 
 
 
diff -pruN 1:0.10.28-3/t/023-rewrite/req-socket.t 1:0.10.29-1/t/023-rewrite/req-socket.t
--- 1:0.10.28-3/t/023-rewrite/req-socket.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/023-rewrite/req-socket.t	2025-12-07 15:22:42.000000000 +0000
@@ -540,3 +540,4 @@ Expect: 100-Continue
 \breceived: hello\b.*?\breceived:  worl\b
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
diff -pruN 1:0.10.28-3/t/025-codecache.t 1:0.10.29-1/t/025-codecache.t
--- 1:0.10.28-3/t/025-codecache.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/025-codecache.t	2025-12-07 15:22:42.000000000 +0000
@@ -855,6 +855,7 @@ qr/\[alert\] \S+ lua_code_cache is off;
 "decrementing the reference count for Lua VM: 2",
 "decrementing the reference count for Lua VM: 1",
 ]
+--- skip_eval: 11:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
@@ -1120,6 +1121,7 @@ qq{lua tcp socket keepalive create conne
 qr/\[alert\] \S+ lua_code_cache is off; this will hurt performance/,
 qr/\blua tcp socket keepalive: free connection pool [0-9A-F]+ for "127.0.0.1:/,
 ]
+--- skip_eval: 7:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
diff -pruN 1:0.10.28-3/t/041-header-filter.t 1:0.10.29-1/t/041-header-filter.t
--- 1:0.10.28-3/t/041-header-filter.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/041-header-filter.t	2025-12-07 15:22:42.000000000 +0000
@@ -847,6 +847,7 @@ GET /t
 --- error_code: 302
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
diff -pruN 1:0.10.28-3/t/043-shdict.t 1:0.10.29-1/t/043-shdict.t
--- 1:0.10.28-3/t/043-shdict.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/043-shdict.t	2025-12-07 15:22:42.000000000 +0000
@@ -158,7 +158,7 @@ hello
         content_by_lua '
             local dogs = ngx.shared.dogs
             dogs:set("foo", 32, 0.01)
-            ngx.location.capture("/sleep/0.01")
+            ngx.location.capture("/sleep/0.011")
             ngx.say(dogs:get("foo"))
         ';
     }
@@ -1131,7 +1131,7 @@ nil nil
         content_by_lua '
             local dogs = ngx.shared.dogs
             dogs:set("foo", 32, 0.01, 255)
-            ngx.location.capture("/sleep/0.01")
+            ngx.location.capture("/sleep/0.011")
             local res, flags = dogs:get("foo")
             ngx.say("res = ", res, ", flags = ", flags)
         ';
diff -pruN 1:0.10.28-3/t/056-flush.t 1:0.10.29-1/t/056-flush.t
--- 1:0.10.28-3/t/056-flush.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/056-flush.t	2025-12-07 15:22:42.000000000 +0000
@@ -43,6 +43,7 @@ hiya
 [error]
 --- error_log
 lua reuse free buf chain, but reallocate memory because 5 >= 0
+--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
diff -pruN 1:0.10.28-3/t/058-tcp-socket.t 1:0.10.29-1/t/058-tcp-socket.t
--- 1:0.10.28-3/t/058-tcp-socket.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/058-tcp-socket.t	2025-12-07 15:22:42.000000000 +0000
@@ -3993,6 +3993,7 @@ hello world
 [error]
 --- error_log
 lua tcp socket read any
+--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "")
 
 
 
@@ -4147,6 +4148,7 @@ hello world
 [error]
 --- error_log
 lua tcp socket calling receiveany() method to read at most 128 bytes
+--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "")
 
 
 
@@ -4222,6 +4224,7 @@ orld
 [error]
 --- error_log
 lua tcp socket calling receiveany() method to read at most 7 bytes
+--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "")
 
 
 
@@ -4563,3 +4566,79 @@ connected: 1
 setkeepalive: 1
 --- no_error_log
 [error]
+
+
+
+=== TEST 75: getfd()
+--- no_http2
+--- http_config
+    lua_package_path "../lua-resty-core/lib/?.lua;;";
+--- config
+    server_tokens off;
+    location /t {
+        #set $port 5000;
+        set $port $TEST_NGINX_SERVER_PORT;
+
+        content_by_lua_block {
+            local sock = ngx.socket.tcp()
+            local port = ngx.var.port
+            local ok, err = sock:connect("127.0.0.1", port)
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+
+            local s, err = sock:getfd()
+            ngx.say("fd: ", s)
+            ngx.say("connected: ", ok)
+
+            local req = "GET /foo HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n"
+            -- req = "OK"
+
+            local bytes, err = sock:send(req)
+            if not bytes then
+                ngx.say("failed to send request: ", err)
+                return
+            end
+
+            ngx.say("request sent: ", bytes)
+
+            while true do
+                local line, err, part = sock:receive()
+                if line then
+                    ngx.say("received: ", line)
+
+                else
+                    ngx.say("failed to receive a line: ", err, " [", part, "]")
+                    break
+                end
+            end
+
+            ok, err = sock:close()
+            ngx.say("close: ", ok, " ", err)
+        }
+    }
+
+    location /foo {
+        content_by_lua 'ngx.say("foo")';
+        more_clear_headers Date;
+    }
+
+--- request
+GET /t
+--- response_body eval
+qr/fd: \d+
+connected: 1
+request sent: 57
+received: HTTP\/1.1 200 OK
+received: Server: nginx
+received: Content-Type: text\/plain
+received: Content-Length: 4
+received: Connection: close
+received: 
+received: foo
+failed to receive a line: closed \[\]
+close: 1 nil
+/ms
+--- no_error_log
+[error]
diff -pruN 1:0.10.28-3/t/062-count.t 1:0.10.29-1/t/062-count.t
--- 1:0.10.28-3/t/062-count.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/062-count.t	2025-12-07 15:22:42.000000000 +0000
@@ -259,7 +259,7 @@ n = 10
 POST /test
 hello world
 --- response_body
-n = 6
+n = 7
 --- no_error_log
 [error]
 --- skip_eval: 3: $ENV{TEST_NGINX_USE_HTTP3}
@@ -460,7 +460,7 @@ worker: 5
 --- request
 GET /test
 --- response_body
-n = 16
+n = 17
 --- no_error_log
 [error]
 
@@ -513,7 +513,7 @@ n = 7
 --- request
 GET /test
 --- response_body
-n = 7
+n = 8
 --- no_error_log
 [error]
 --- skip_eval: 3:$ENV{TEST_NGINX_USE_HTTP3}
diff -pruN 1:0.10.28-3/t/067-req-socket.t 1:0.10.29-1/t/067-req-socket.t
--- 1:0.10.28-3/t/067-req-socket.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/067-req-socket.t	2025-12-07 15:22:42.000000000 +0000
@@ -529,6 +529,7 @@ Expect: 100-Continue
 \breceived: hello\b.*?\breceived:  worl\b
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
@@ -1176,3 +1177,32 @@ GET /t
 received: received: abc
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "")
+
+
+
+=== TEST 19: getfd
+--- http_config
+    lua_package_path "../lua-resty-core/lib/?.lua;;";
+--- config
+    location /t {
+        content_by_lua_block {
+            local sock, err = ngx.req.socket()
+            if sock then
+                ngx.say("got the request socket")
+            else
+                ngx.say("failed to get the request socket: ", err)
+            end
+
+            ngx.say(sock:getfd())
+        }
+    }
+--- request
+POST /t
+hello world
+--- response_body eval
+qr/\Agot the request socket
+\d+
+\z/ms
+--- no_error_log
+[error]
diff -pruN 1:0.10.28-3/t/071-idle-socket.t 1:0.10.29-1/t/071-idle-socket.t
--- 1:0.10.28-3/t/071-idle-socket.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/071-idle-socket.t	2025-12-07 15:22:42.000000000 +0000
@@ -431,3 +431,4 @@ failed to set keepalive: unread data in
 }
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "")
diff -pruN 1:0.10.28-3/t/072-conditional-get.t 1:0.10.29-1/t/072-conditional-get.t
--- 1:0.10.28-3/t/072-conditional-get.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/072-conditional-get.t	2025-12-07 15:22:42.000000000 +0000
@@ -88,3 +88,4 @@ delete thread 1
 say failed: nginx output filter error
 --- no_error_log
 [error]
+--- skip_eval: 5:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
diff -pruN 1:0.10.28-3/t/100-client-abort.t 1:0.10.29-1/t/100-client-abort.t
--- 1:0.10.28-3/t/100-client-abort.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/100-client-abort.t	2025-12-07 15:22:42.000000000 +0000
@@ -955,6 +955,7 @@ GET /t
 [alert]
 --- error_log
 say failed: nginx output filter error
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
@@ -983,6 +984,7 @@ GET /t
 [alert]
 --- error_log
 print failed: nginx output filter error
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
@@ -1042,6 +1044,7 @@ GET /t
 [alert]
 --- error_log
 flush succeeded
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
 
 
 
@@ -1072,3 +1075,4 @@ GET /t
 eof succeeded
 --- error_log
 eof failed: nginx output filter error
+--- skip_eval: 4:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} =~ /w/)
diff -pruN 1:0.10.28-3/t/116-raw-req-socket.t 1:0.10.29-1/t/116-raw-req-socket.t
--- 1:0.10.28-3/t/116-raw-req-socket.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/116-raw-req-socket.t	2025-12-07 15:22:42.000000000 +0000
@@ -12,7 +12,7 @@ use Test::Nginx::Socket::Lua $SkipReason
 
 repeat_each(2);
 
-plan tests => repeat_each() * 43;
+plan tests => repeat_each() * 46;
 
 our $HtmlDir = html_dir;
 
@@ -977,3 +977,90 @@ GET /t
 msg: 1: received: hello
 --- no_error_log
 [error]
+--- skip_eval: 3:defined($ENV{MOCKEAGAIN}) && ($ENV{MOCKEAGAIN} ne "")
+
+
+
+=== TEST 17: getfd()
+--- config
+    server_tokens off;
+    location = /t {
+        #set $port 5000;
+        set $port $TEST_NGINX_SERVER_PORT;
+
+        content_by_lua_block {
+            local sock = ngx.socket.tcp()
+            local port = ngx.var.port
+            local ok, err = sock:connect("127.0.0.1", port)
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+
+            local req = "GET /mysock HTTP/1.1\r\nUpgrade: mysock\r\nHost: localhost\r\nConnection: close\r\n\r\nhello"
+            -- req = "OK"
+
+            local bytes, err = sock:send(req)
+            if not bytes then
+                ngx.say("failed to send request: ", err)
+                return
+            end
+
+            local reader = sock:receiveuntil("\r\n\r\n")
+            local data, err, partial = reader()
+            if not data then
+                ngx.say("no response header found")
+                return
+            end
+
+            local msg, err = sock:receive()
+            if not msg then
+                ngx.say("failed to receive: ", err)
+                return
+            end
+
+            ngx.say("msg: ", msg)
+
+            ok, err = sock:close()
+            if not ok then
+                ngx.say("failed to close socket: ", err)
+                return
+            end
+        }
+    }
+
+    location = /mysock {
+        content_by_lua_block {
+            ngx.status = 101
+            ngx.send_headers()
+            ngx.flush(true)
+            ngx.req.read_body()
+            local sock, err = ngx.req.socket(true)
+            if not sock then
+                ngx.log(ngx.ERR, "server: failed to get raw req socket: ", err)
+                return
+            end
+
+            local s = sock:getfd()
+            local data, err = sock:receive(5)
+            if not data then
+                ngx.log(ngx.ERR, "server: failed to receive: ", err)
+                return
+            end
+
+            local bytes, err = sock:send("fd: " .. tostring(s) .. " 1: received: " .. data .. "\n")
+            if not bytes then
+                ngx.log(ngx.ERR, "server: failed to send: ", err)
+                return
+            end
+        }
+        more_clear_headers Date;
+    }
+
+--- request
+GET /t
+--- response_body eval
+qr/\Amsg: fd: \d+ 1: received: hello
+/ms
+--- no_error_log
+[error]
diff -pruN 1:0.10.28-3/t/129-ssl-socket.t 1:0.10.29-1/t/129-ssl-socket.t
--- 1:0.10.28-3/t/129-ssl-socket.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/129-ssl-socket.t	2025-12-07 15:22:42.000000000 +0000
@@ -1963,7 +1963,7 @@ $::TestCertificate"
 --- grep_error_log eval: qr/lua ssl (?:set|save|free) session: [0-9A-F]+/
 --- grep_error_log_out
 --- error_log eval
-qr/SSL_do_handshake\(\) failed .*?(unknown protocol|wrong version number)/
+qr/SSL_do_handshake\(\) failed .*?(unknown protocol|wrong version number|routines::record layer failure)/
 --- no_error_log
 lua ssl server name:
 SSL reused session
@@ -2944,3 +2944,105 @@ SSL reused session
 [alert]
 [emerg]
 --- timeout: 10
+
+
+
+=== TEST 35: lua_ssl_key_log directive
+--- skip_openssl: 8: < 1.1.1
+--- http_config
+    server {
+        listen              $TEST_NGINX_SERVER_SSL_PORT ssl;
+        server_name         test.com;
+        ssl_certificate     $TEST_NGINX_CERT_DIR/cert/test.crt;
+        ssl_certificate_key $TEST_NGINX_CERT_DIR/cert/test.key;
+        ssl_protocols       TLSv1.3;
+
+        location / {
+            content_by_lua_block {
+                ngx.exit(200)
+            }
+        }
+    }
+--- config
+    server_tokens off;
+    lua_ssl_protocols TLSv1.3;
+    lua_ssl_key_log sslkey.log;
+
+    location /t {
+        content_by_lua_block {
+            local sock = ngx.socket.tcp()
+            sock:settimeout(2000)
+
+            do
+                local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_SERVER_SSL_PORT)
+                if not ok then
+                    ngx.say("failed to connect: ", err)
+                    return
+                end
+
+                ngx.say("connected: ", ok)
+
+                local session, err = sock:sslhandshake(nil, "test.com")
+                if not session then
+                    ngx.say("failed to do SSL handshake: ", err)
+                    return
+                end
+
+                ngx.say("ssl handshake: ", type(session))
+
+                local req = "GET / HTTP/1.1\r\nHost: test.com\r\nConnection: close\r\n\r\n"
+                local bytes, err = sock:send(req)
+                if not bytes then
+                    ngx.say("failed to send http request: ", err)
+                    return
+                end
+
+                ngx.say("sent http request: ", bytes, " bytes.")
+
+                local line, err = sock:receive()
+                if not line then
+                    ngx.say("failed to receive response status line: ", err)
+                    return
+                end
+
+                ngx.say("received: ", line)
+
+                local ok, err = sock:close()
+                ngx.say("close: ", ok, " ", err)
+
+                local f, err = io.open("$TEST_NGINX_SERVER_ROOT/conf/sslkey.log", "r")
+                if not f then
+                    ngx.log(ngx.ERR, "failed to open sslkey.log: ", err)
+                    return
+                end
+
+                local key_log = f:read("*a")
+                ngx.say(key_log)
+                f:close()
+            end  -- do
+            collectgarbage()
+        }
+    }
+--- request
+GET /t
+--- response_body_like
+connected: 1
+ssl handshake: cdata
+sent http request: 53 bytes.
+received: HTTP/1.1 200 OK
+close: 1 nil
+SERVER_HANDSHAKE_TRAFFIC_SECRET [0-9a-z\s]+
+EXPORTER_SECRET [0-9a-z\s]+
+SERVER_TRAFFIC_SECRET_0 [0-9a-z\s]+
+CLIENT_HANDSHAKE_TRAFFIC_SECRET [0-9a-z\s]+
+CLIENT_TRAFFIC_SECRET_0 [0-9a-z\s]+
+
+--- log_level: debug
+--- error_log eval
+['lua ssl server name: "test.com"',
+qr/SSL: TLSv1.3, cipher: "TLS_AES_256_GCM_SHA384 TLSv1.3/]
+--- no_error_log
+SSL reused session
+[error]
+[alert]
+--- timeout: 10
diff -pruN 1:0.10.28-3/t/139-ssl-cert-by.t 1:0.10.29-1/t/139-ssl-cert-by.t
--- 1:0.10.28-3/t/139-ssl-cert-by.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/139-ssl-cert-by.t	2025-12-07 15:22:42.000000000 +0000
@@ -7,6 +7,9 @@ repeat_each(3);
 # All these tests need to have new openssl
 my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
 my $openssl_version = eval { `$NginxBinary -V 2>&1` };
+if ($openssl_version =~ m/BoringSSL/) {
+    $ENV{TEST_NGINX_USE_BORINGSSL} = 1;
+}
 
 if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) {
     plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1");
@@ -16,6 +19,7 @@ if ($openssl_version =~ m/built with Ope
 
 $ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
 $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;
+$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6;
 
 #log_level 'warn';
 log_level 'debug';
@@ -578,7 +582,7 @@ failed to do SSL handshake: handshake fa
 
 --- error_log eval
 [
-'lua_certificate_by_lua: handler return value: -1, cert cb exit code: 0',
+'ssl_certificate_by_lua: handler return value: -1, cert cb exit code: 0',
 qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,
 'lua exit with code -1',
 ]
@@ -719,7 +723,7 @@ failed to do SSL handshake: handshake fa
 
 --- error_log eval
 [
-'lua_certificate_by_lua: cert cb exit code: 0',
+'ssl_certificate_by_lua: cert cb exit code: 0',
 qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,
 'lua exit with code -1',
 ]
@@ -790,7 +794,7 @@ failed to do SSL handshake: handshake fa
 --- error_log eval
 [
 'runtime error: ssl_certificate_by_lua(nginx.conf:28):2: bad bad bad',
-'lua_certificate_by_lua: handler return value: 500, cert cb exit code: 0',
+'ssl_certificate_by_lua: handler return value: 500, cert cb exit code: 0',
 qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,
 qr/context: ssl_certificate_by_lua\*, client: \d+\.\d+\.\d+\.\d+, server: \d+\.\d+\.\d+\.\d+:\d+/,
 ]
@@ -862,7 +866,7 @@ failed to do SSL handshake: handshake fa
 --- error_log eval
 [
 'runtime error: ssl_certificate_by_lua(nginx.conf:28):3: bad bad bad',
-'lua_certificate_by_lua: cert cb exit code: 0',
+'ssl_certificate_by_lua: cert cb exit code: 0',
 qr/(\[info\] .*? SSL_do_handshake\(\) failed .*?cert cb error|routines:OPENSSL_internal:CERT_CB_ERROR)/,
 ]
 
@@ -2075,7 +2079,7 @@ received: foo
 close: 1 nil
 
 --- error_log
-client socket file: 
+client socket file:
 
 --- no_error_log
 [error]
@@ -2321,3 +2325,445 @@ ssl handshake: cdata
 uthread: hello from f()
 uthread: killed
 uthread: failed to kill: already waited or killed
+
+
+
+=== TEST 27: ssl_certificate_by_lua* with TCP cosocket (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_certificate_by_lua_block {
+        print("ssl certificate: TCP cosocket test start")
+
+        local sock = ngx.socket.tcp()
+        sock:settimeout(2000)
+
+        local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to connect to memc: ", err)
+            return
+        end
+
+        local bytes, err = sock:send("flush_all\r\n")
+        if not bytes then
+            ngx.log(ngx.ERR, "failed to send flush_all command: ", err)
+            return
+        end
+
+        local res, err = sock:receive()
+        if not res then
+            ngx.log(ngx.ERR, "failed to receive memc reply: ", err)
+            return
+        end
+
+        print("ssl certificate: received TCP memc reply: ", res)
+        sock:close()
+        print("ssl certificate: TCP cosocket test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl certificate: TCP cosocket test start|ssl certificate: received TCP memc reply: OK|ssl certificate: TCP cosocket test done|test completed)/
+--- grep_error_log_out
+ssl certificate: TCP cosocket test start
+ssl certificate: received TCP memc reply: OK
+ssl certificate: TCP cosocket test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 28: ssl_certificate_by_lua* with UDP cosocket (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_certificate_by_lua_block {
+        print("ssl certificate: UDP cosocket test start")
+
+        local sock = ngx.socket.udp()
+        sock:settimeout(1000)
+
+        local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to connect to memc: ", err)
+            return
+        end
+
+        local req = "\0\1\0\0\0\1\0\0flush_all\r\n"
+        local ok, err = sock:send(req)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to send flush_all to memc: ", err)
+            return
+        end
+
+        local res, err = sock:receive()
+        if not res then
+            ngx.log(ngx.ERR, "failed to receive memc reply: ", err)
+            return
+        end
+
+        print("ssl certificate: received UDP memc reply of ", #res, " bytes")
+        sock:close()
+        print("ssl certificate: UDP cosocket test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl certificate: UDP cosocket test start|ssl certificate: received UDP memc reply of \d+ bytes|ssl certificate: UDP cosocket test done|test completed)/
+--- grep_error_log_out eval
+[
+qr/ssl certificate: UDP cosocket test start/,
+qr/ssl certificate: received UDP memc reply of 12 bytes/,
+qr/ssl certificate: UDP cosocket test done/,
+qr/test completed/,
+]
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 29: ssl_certificate_by_lua* with ngx.timer (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_certificate_by_lua_block {
+        print("ssl certificate: timer test start")
+
+        local function timer_handler()
+            print("ssl certificate: timer executed")
+        end
+
+        local ok, err = ngx.timer.at(0, timer_handler)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to create timer: ", err)
+            return
+        end
+
+        print("ssl certificate: timer created")
+        print("ssl certificate: timer test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl certificate: timer test start|ssl certificate: timer created|ssl certificate: timer test done|ssl certificate: timer executed|test completed)/
+--- grep_error_log_out
+ssl certificate: timer test start
+ssl certificate: timer created
+ssl certificate: timer test done
+ssl certificate: timer executed
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 30: ssl_certificate_by_lua* with user threads (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_certificate_by_lua_block {
+        print("ssl certificate: uthread test start")
+
+        local function worker()
+            ngx.sleep(0.01)
+            print("ssl certificate: uthread worker executed")
+            return "worker_result"
+        end
+
+        local t, err = ngx.thread.spawn(worker)
+        if not t then
+            ngx.log(ngx.ERR, "failed to spawn thread: ", err)
+            return
+        end
+
+        print("ssl certificate: uthread spawned")
+
+        local ok, res = ngx.thread.wait(t)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to wait thread: ", res)
+            return
+        end
+
+        print("ssl certificate: uthread result: ", res)
+        print("ssl certificate: uthread test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl certificate: uthread test start|ssl certificate: uthread spawned|ssl certificate: uthread worker executed|ssl certificate: uthread result: worker_result|ssl certificate: uthread test done|test completed)/
+--- grep_error_log_out
+ssl certificate: uthread test start
+ssl certificate: uthread spawned
+ssl certificate: uthread worker executed
+ssl certificate: uthread result: worker_result
+ssl certificate: uthread test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 31: ssl_certificate_by_lua* with coroutines (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_certificate_by_lua_block {
+        print("ssl certificate: coroutine test start")
+
+        local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield
+
+        local function coro_func()
+            local cnt = 0
+            for i = 1, 3 do
+                print("ssl certificate: coro yield: ", cnt)
+                cy()
+                cnt = cnt + 1
+            end
+            return "coro_done"
+        end
+
+        local c = cc(coro_func)
+        for i = 1, 4 do
+            print("ssl certificate: coro resume, status: ", coroutine.status(c))
+            local ok, res = cr(c)
+            if not ok then
+                print("ssl certificate: coro error: ", res)
+                break
+            end
+            if coroutine.status(c) == "dead" then
+                print("ssl certificate: coro result: ", res)
+                break
+            end
+        end
+
+        print("ssl certificate: coroutine test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl certificate: coroutine test start|ssl certificate: coro resume, status: \w+|ssl certificate: coro yield: \d+|ssl certificate: coro result: coro_done|ssl certificate: coroutine test done|test completed)/
+--- grep_error_log_out
+ssl certificate: coroutine test start
+ssl certificate: coro resume, status: suspended
+ssl certificate: coro yield: 0
+ssl certificate: coro resume, status: suspended
+ssl certificate: coro yield: 1
+ssl certificate: coro resume, status: suspended
+ssl certificate: coro yield: 2
+ssl certificate: coro resume, status: suspended
+ssl certificate: coro result: coro_done
+ssl certificate: coroutine test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 32: ssl_certificate_by_lua* without yield API (simple logic)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_certificate_by_lua_block {
+        print("ssl certificate: simple test start")
+
+        -- Simple calculations without yield
+        local sum = 0
+        for i = 1, 10 do
+            sum = sum + i
+        end
+
+        print("ssl certificate: calculated sum: ", sum)
+
+        -- String operations
+        local str = "hello"
+        str = str .. " world"
+        print("ssl certificate: concatenated string: ", str)
+
+        -- Table operations
+        local t = {a = 1, b = 2, c = 3}
+        local count = 0
+        for k, v in pairs(t) do
+            count = count + v
+        end
+        print("ssl certificate: table sum: ", count)
+
+        print("ssl certificate: simple test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl certificate: simple test start|ssl certificate: calculated sum: 55|ssl certificate: concatenated string: hello world|ssl certificate: table sum: 6|ssl certificate: simple test done|test completed)/
+--- grep_error_log_out
+ssl certificate: simple test start
+ssl certificate: calculated sum: 55
+ssl certificate: concatenated string: hello world
+ssl certificate: table sum: 6
+ssl certificate: simple test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 33: ssl_certificate_by_lua* with multiple network operations (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3} || $ENV{TEST_NGINX_USE_BORINGSSL}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_certificate_by_lua_block {
+        print("ssl certificate: multiple network test start")
+
+        -- First TCP operation
+        local sock1 = ngx.socket.tcp()
+        sock1:settimeout(2000)
+        local ok, err = sock1:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if ok then
+            local bytes, err = sock1:send("version\r\n")
+            if bytes then
+                local res, err = sock1:receive()
+                if res then
+                    print("ssl certificate: TCP1 version: ", res)
+                end
+            end
+            sock1:close()
+        end
+
+        ngx.sleep(0.01)  -- Small delay
+
+        -- Second UDP operation
+        local sock2 = ngx.socket.udp()
+        sock2:settimeout(1000)
+        local ok, err = sock2:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if ok then
+            local req = "\0\1\0\0\0\1\0\0version\r\n"
+            local ok, err = sock2:send(req)
+            if ok then
+                local res, err = sock2:receive()
+                if res then
+                    print("ssl certificate: UDP version reply length: ", #res)
+                end
+            end
+            sock2:close()
+        end
+
+        print("ssl certificate: multiple network test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl certificate: multiple network test start|ssl certificate: TCP1 version: VERSION|ssl certificate: UDP version reply length: \d+|ssl certificate: multiple network test done|test completed)/
+--- grep_error_log_out eval
+[
+qr/ssl certificate: multiple network test start/,
+qr/ssl certificate: TCP1 version: VERSION/,
+qr/ssl certificate: UDP version reply length: \d+/,
+qr/ssl certificate: multiple network test done/,
+qr/test completed/,
+]
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
diff -pruN 1:0.10.28-3/t/140-ssl-c-api.t 1:0.10.29-1/t/140-ssl-c-api.t
--- 1:0.10.28-3/t/140-ssl-c-api.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/140-ssl-c-api.t	2025-12-07 15:22:42.000000000 +0000
@@ -10,9 +10,12 @@ my $openssl_version = eval { `$NginxBina
 
 if ($openssl_version =~ m/built with OpenSSL (0|1\.0\.(?:0|1[^\d]|2[a-d]).*)/) {
     plan(skip_all => "too old OpenSSL, need 1.0.2e, was $1");
-
+} elsif ($openssl_version =~ m/BoringSSL/) {
+    $ENV{TEST_NGINX_USE_BORINGSSL} = 1;
+    plan tests => repeat_each() * (blocks() * 6 - 6);
 } else {
-    plan tests => repeat_each() * (blocks() * 5 - 1);
+    plan tests => repeat_each() * (blocks() * 5 - 5);
+    $ENV{TEST_NGINX_USE_OPENSSL} = 1;
 }
 
 $ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
@@ -77,6 +80,8 @@ ffi.cdef[[
     int ngx_http_lua_ffi_ssl_client_random(ngx_http_request_t *r,
         unsigned char *out, size_t *outlen, char **err);
 
+    int ngx_http_lua_ffi_req_shared_ssl_ciphers(void *r, uint16_t *ciphers,
+        uint16_t *nciphers, int filter_grease, char **err);
 ]]
 _EOC_
     }
@@ -1212,6 +1217,7 @@ lua ssl server name: "test.com"
 
 
 === TEST 10: Raw SSL pointer
+--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
 --- http_config
     server {
         listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
@@ -1777,3 +1783,294 @@ SUCCESS
 --- no_error_log
 [error]
 [alert]
+
+
+
+=== TEST 15: Get supported ciphers
+--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+        server_name test.com;
+        ssl_certificate ../../cert/test.crt;
+        ssl_certificate_key ../../cert/test.key;
+        ssl_protocols TLSv1.2;
+        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
+
+        server_tokens off;  
+        
+        location /ciphers { 
+            content_by_lua_block {
+                require "defines"
+                local ffi = require "ffi"
+                local cjson = require "cjson.safe"
+                local base = require "resty.core.base"
+                local get_request = base.get_request
+
+                local MAX_CIPHERS = 64
+                local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
+                local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
+                local err = ffi.new("char*[1]")
+
+                local r = get_request()
+                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)
+
+                if ret ~= 0 then
+                    ngx.log(ngx.ERR, "error: ", ffi.string(err[0]))
+                    return
+                end
+
+                local res = {}
+                for i = 0, nciphers[0] - 1 do
+                    local cipher_id = string.format("%04x", ciphers[i])
+                    table.insert(res, cipher_id)
+                end
+
+                ngx.say(cjson.encode(res))
+            }
+        }
+    }
+--- config
+    server_tokens off;
+    location /t {
+        proxy_ssl_protocols TLSv1.2;
+        proxy_ssl_session_reuse     off;        
+        proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;
+        proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;
+    }
+--- request
+GET /t
+--- response_body_like
+\["c02f","c02b"\]
+--- error_log chomp
+TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD"
+
+
+
+=== TEST 16: SSL cipher API error handling (no SSL)
+--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
+--- config
+    location /t {
+        content_by_lua_block {
+            require "defines"        
+            local ffi = require "ffi"
+            
+            local ciphers = ffi.new("uint16_t[64]")
+            local nciphers = ffi.new("uint16_t[1]", 64)
+            local err = ffi.new("char*[1]")
+
+            -- use nil request to trigger error
+            local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(nil, ciphers, nciphers, 0, err)
+
+            ngx.say("ret: ", ret)
+            if err[0] ~= nil then
+                ngx.say("err: ", ffi.string(err[0]))
+            end
+        }
+    }
+--- request
+GET /t
+--- response_body
+ret: -1
+err: bad request
+
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 17: Buffer overflow handling
+--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+        server_name test.com;
+        ssl_certificate ../../cert/test.crt;
+        ssl_certificate_key ../../cert/test.key;
+        ssl_protocols TLSv1.2;
+        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
+
+        server_tokens off;  
+
+        
+        location /ciphers { 
+            content_by_lua_block {
+                require "defines"            
+                local ffi = require "ffi"
+                local base = require "resty.core.base"
+                local get_request = base.get_request
+                local cjson = require "cjson.safe"
+    
+                local MAX_CIPHERS = 64
+                local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
+                local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
+                local err = ffi.new("char*[1]")
+
+                local r = get_request()
+                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)
+
+                if ret ~= 0 then
+                    ngx.log(ngx.ERR, "error: ", ffi.string(err[0]))
+                    return
+                end
+                local res = {}
+                for i = 0, nciphers[0] - 1 do
+                    local cipher_id = string.format("%04x", ciphers[i])
+
+                    table.insert(res, cipher_id)
+                end
+                ngx.say(cjson.encode(res))
+            }
+        }
+    }
+--- config
+    server_tokens off;
+    location /t {
+        proxy_ssl_protocols TLSv1.2;
+        proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256;
+        proxy_ssl_session_reuse off;    
+        proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;
+    }
+--- request
+GET /t
+--- response_body_like
+\["c02f"\]
+--- error_code: 200
+--- error_log chomp
+TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD"
+
+
+
+=== TEST 18: BORINGSSL error handling
+--- skip_eval: 8:$ENV{TEST_NGINX_USE_OPENSSL}
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+        server_name test.com;
+        ssl_certificate ../../cert/test.crt;
+        ssl_certificate_key ../../cert/test.key;
+        ssl_protocols TLSv1.2 TLSv1.3;
+        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
+
+        server_tokens off;  
+
+        
+        location /ciphers { 
+            content_by_lua_block {
+                require "defines"            
+                local ffi = require "ffi"
+                local base = require "resty.core.base"
+                local get_request = base.get_request
+
+                local MAX_CIPHERS = 64
+                local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
+                local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
+                local err = ffi.new("char*[1]")
+
+                local r = get_request()
+                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)
+
+                if ret ~= 0 then
+                    ngx.say("Error: ", ffi.string(err[0]))
+                    return
+                end
+            
+            }
+        }              
+    }
+--- config
+    server_tokens off;
+    location /t {
+        proxy_ssl_protocols TLSv1.2 TLSv1.3;
+        proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;
+        proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;
+    }
+--- request
+GET /t
+--- response_body_like chomp
+Error: BoringSSL is not supported for SSL cipher operations
+--- error_code: 200
+
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 19: Get supported ciphers with GREASE filtering
+--- skip_eval: 8:$ENV{TEST_NGINX_USE_BORINGSSL}
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+        server_name test.com;
+        ssl_certificate ../../cert/test.crt;
+        ssl_certificate_key ../../cert/test.key;
+        ssl_protocols TLSv1.2;
+        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384;
+
+        server_tokens off;
+        
+        location /ciphers {
+            content_by_lua_block {
+                require "defines"
+                local ffi = require "ffi"
+                local cjson = require "cjson.safe"
+                local base = require "resty.core.base"
+                local get_request = base.get_request
+
+                local MAX_CIPHERS = 64
+                local ciphers = ffi.new("uint16_t[?]", MAX_CIPHERS)
+                local nciphers = ffi.new("uint16_t[1]", MAX_CIPHERS)
+                local err = ffi.new("char*[1]")
+
+                local r = get_request()
+                -- Test without GREASE filtering
+                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 0, err)
+                if ret ~= 0 then
+                    ngx.log(ngx.ERR, "error without filtering: ", ffi.string(err[0]))
+                    return
+                end
+
+                local res_no_filter = {}
+                for i = 0, nciphers[0] - 1 do
+                    local cipher_id = string.format("%04x", ciphers[i])
+                    table.insert(res_no_filter, cipher_id)
+                end
+
+                -- Reset buffers
+                nciphers[0] = MAX_CIPHERS
+                
+                -- Test with GREASE filtering
+                local ret = ffi.C.ngx_http_lua_ffi_req_shared_ssl_ciphers(r, ciphers, nciphers, 1, err)
+                if ret ~= 0 then
+                    ngx.log(ngx.ERR, "error with filtering: ", ffi.string(err[0]))
+                    return
+                end
+
+                local res_with_filter = {}
+                for i = 0, nciphers[0] - 1 do
+                    local cipher_id = string.format("%04x", ciphers[i])
+                    table.insert(res_with_filter, cipher_id)
+                end
+
+                ngx.say("without_filter:", cjson.encode(res_no_filter))
+                ngx.say("with_filter:", cjson.encode(res_with_filter))
+            }
+        }
+    }
+--- config
+    server_tokens off;
+    location /t {
+        proxy_ssl_protocols TLSv1.2;
+        proxy_ssl_session_reuse     off;
+        proxy_ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256;
+        proxy_pass https://unix:$TEST_NGINX_HTML_DIR/nginx.sock:/ciphers;
+    }
+--- request
+GET /t
+--- response_body_like
+without_filter:\[.*\]
+with_filter:\[.*\]
+--- error_log chomp
+TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(128) Mac=AEAD"
diff -pruN 1:0.10.28-3/t/143-ssl-session-fetch.t 1:0.10.29-1/t/143-ssl-session-fetch.t
--- 1:0.10.28-3/t/143-ssl-session-fetch.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/143-ssl-session-fetch.t	2025-12-07 15:22:42.000000000 +0000
@@ -9,6 +9,12 @@ repeat_each(3);
 
 plan tests => repeat_each() * (blocks() * 6) - 3;
 
+my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
+my $openssl_version = eval { `$NginxBinary -V 2>&1` };
+if ($openssl_version =~ m/BoringSSL/) {
+    $ENV{TEST_NGINX_USE_BORINGSSL} = 1;
+}
+
 $ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
 
 $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;
@@ -138,11 +144,12 @@ ssl_session_fetch_by_lua\(nginx\.conf:25
 
         server_tokens off;
     }
+--- skip_eval: 6:$ENV{TEST_NGINX_USE_BORINGSSL}
 --- config
     server_tokens off;
     resolver $TEST_NGINX_RESOLVER ipv6=off;
     lua_ssl_trusted_certificate $TEST_NGINX_CERT_DIR/cert/test.crt;
-    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2;
+    lua_ssl_protocols TLSv1 TLSv1.1 TLSV1.2 TLSv1.3;
 
     location /t {
         set $port $TEST_NGINX_MEMCACHED_PORT;
@@ -199,6 +206,7 @@ qr/elapsed in ssl fetch session by lua:
 [error]
 [alert]
 [emerg]
+--- skip_openssl: 6: > 1.1.1w
 
 
 
@@ -388,6 +396,7 @@ qr/received memc reply: OK/s
 [alert]
 [error]
 [emerg]
+--- skip_openssl: 6: > 1.1.1w
 
 
 
@@ -635,6 +644,7 @@ qr/ssl_session_fetch_by_lua\*: sess get
 should never reached here
 [alert]
 [emerg]
+--- skip_openssl: 6: > 1.1.1w
 
 
 
@@ -804,6 +814,7 @@ ssl_session_fetch_by_lua*: sess get cb e
 should never reached here
 [alert]
 [emerg]
+--- skip_openssl: 6: > 1.1.1w
 
 
 
@@ -991,6 +1002,7 @@ ssl store session by lua is running!
 [error]
 [alert]
 [emerg]
+--- skip_openssl: 6: > 1.1.1w
 
 
 
@@ -1399,6 +1411,7 @@ qr/elapsed in ssl_session_fetch_by_lua\*
 [error]
 [alert]
 [emerg]
+--- skip_openssl: 6: > 1.1.1w
 
 
 
@@ -1494,6 +1507,7 @@ close: 1 nil
 [alert]
 [error]
 [emerg]
+--- skip_openssl: 6: > 1.1.1w
 
 
 
@@ -1695,3 +1709,4 @@ uthread: failed to kill: already waited
 [alert]
 [error]
 [emerg]
+--- skip_openssl: 6: > 1.1.1w
diff -pruN 1:0.10.28-3/t/166-ssl-client-hello.t 1:0.10.29-1/t/166-ssl-client-hello.t
--- 1:0.10.28-3/t/166-ssl-client-hello.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/166-ssl-client-hello.t	2025-12-07 15:22:42.000000000 +0000
@@ -12,12 +12,15 @@ if ($openssl_version =~ m/built with Ope
     plan(skip_all => "too old OpenSSL, need 1.1.1, was $1");
 } elsif ($openssl_version =~ m/running with BoringSSL/) {
     plan(skip_all => "does not support BoringSSL");
+} elsif ($ENV{TEST_NGINX_USE_HTTP3}) {
+    plan tests => repeat_each() * (blocks() * 6 + 6);
 } else {
-    plan tests => repeat_each() * (blocks() * 6 + 8);
+    plan tests => repeat_each() * (blocks() * 6 + 10);
 }
 
 $ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
 $ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;
+$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6;
 
 #log_level 'warn';
 log_level 'debug';
@@ -580,7 +583,7 @@ failed to do SSL handshake: handshake fa
 
 --- error_log eval
 [
-'lua_client_hello_by_lua: handler return value: -1, client hello cb exit code: 0',
+'ssl_client_hello_by_lua: handler return value: -1, client hello cb exit code: 0',
 qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/,
 'lua exit with code -1',
 ]
@@ -721,7 +724,7 @@ failed to do SSL handshake: handshake fa
 
 --- error_log eval
 [
-'lua_client_hello_by_lua: client hello cb exit code: 0',
+'ssl_client_hello_by_lua: client hello cb exit code: 0',
 qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/,
 'lua exit with code -1',
 ]
@@ -792,7 +795,7 @@ failed to do SSL handshake: handshake fa
 --- error_log eval
 [
 'runtime error: ssl_client_hello_by_lua(nginx.conf:28):2: bad bad bad',
-'lua_client_hello_by_lua: handler return value: 500, client hello cb exit code: 0',
+'ssl_client_hello_by_lua: handler return value: 500, client hello cb exit code: 0',
 qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/,
 qr/context: ssl_client_hello_by_lua\*, client: \d+\.\d+\.\d+\.\d+, server: \d+\.\d+\.\d+\.\d+:\d+/,
 ]
@@ -864,7 +867,7 @@ failed to do SSL handshake: handshake fa
 --- error_log eval
 [
 'runtime error: ssl_client_hello_by_lua(nginx.conf:28):3: bad bad bad',
-'lua_client_hello_by_lua: client hello cb exit code: 0',
+'ssl_client_hello_by_lua: client hello cb exit code: 0',
 qr/\[info\] .*? SSL_do_handshake\(\) failed .*?callback failed/,
 ]
 
@@ -1667,11 +1670,11 @@ ssl client hello by lua is running!
 
 === TEST 20: use ssl_client_hello_by_lua* on the http {} level with non-ssl server
 --- http_config
-    ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") }
     ssl_certificate ../../cert/test.crt;
     ssl_certificate_key ../../cert/test.key;
     server {
         listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        ssl_client_hello_by_lua_block { print("ssl client hello by lua is running!") }
         server_name   test.com;
         server_tokens off;
         location /foo {
@@ -1743,7 +1746,6 @@ close: 1 nil
 ssl client hello by lua is running!
 [error]
 [alert]
---- skip_eval: 5:$ENV{TEST_NGINX_USE_HTTP3}
 
 
 
@@ -2323,7 +2325,7 @@ received: foo
 close: 1 nil
 
 --- error_log
-client socket file: 
+client socket file:
 
 --- no_error_log
 [error]
@@ -2629,7 +2631,7 @@ ssl handshake: boolean
 
 --- error_log eval
 [
-'lua_client_hello_by_lua: handler return value: 0, client hello cb exit code: 1',
+'ssl_client_hello_by_lua: handler return value: 0, client hello cb exit code: 1',
 qr/\[debug\] .*? SSL_do_handshake: 1/,
 'lua exit with code 0',
 ]
@@ -2637,3 +2639,815 @@ qr/\[debug\] .*? SSL_do_handshake: 1/,
 should never reached here
 [alert]
 [emerg]
+
+
+
+=== TEST 31: ssl_client_hello_by_lua* can yield when reading early data
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("ssl client hello by lua is running!")
+        ngx.sleep(0.1)
+        print("ssl client hello by lua is done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl client hello by lua is running!|ssl client hello by lua is done|test completed)/
+--- grep_error_log_out
+ssl client hello by lua is running!
+ssl client hello by lua is done
+test completed
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 32: ssl_client_hello_by_lua* with TCP cosocket (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("ssl client hello: TCP cosocket test start")
+
+        local sock = ngx.socket.tcp()
+        sock:settimeout(2000)
+
+        local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to connect to memc: ", err)
+            return
+        end
+
+        local bytes, err = sock:send("flush_all\r\n")
+        if not bytes then
+            ngx.log(ngx.ERR, "failed to send flush_all command: ", err)
+            return
+        end
+
+        local res, err = sock:receive()
+        if not res then
+            ngx.log(ngx.ERR, "failed to receive memc reply: ", err)
+            return
+        end
+
+        print("ssl client hello: received TCP memc reply: ", res)
+        sock:close()
+        print("ssl client hello: TCP cosocket test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl client hello: TCP cosocket test start|ssl client hello: received TCP memc reply: OK|ssl client hello: TCP cosocket test done|test completed)/
+--- grep_error_log_out
+ssl client hello: TCP cosocket test start
+ssl client hello: received TCP memc reply: OK
+ssl client hello: TCP cosocket test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 33: ssl_client_hello_by_lua* with UDP cosocket (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("ssl client hello: UDP cosocket test start")
+
+        local sock = ngx.socket.udp()
+        sock:settimeout(1000)
+
+        local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to connect to memc: ", err)
+            return
+        end
+
+        local req = "\0\1\0\0\0\1\0\0flush_all\r\n"
+        local ok, err = sock:send(req)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to send flush_all to memc: ", err)
+            return
+        end
+
+        local res, err = sock:receive()
+        if not res then
+            ngx.log(ngx.ERR, "failed to receive memc reply: ", err)
+            return
+        end
+
+        print("ssl client hello: received UDP memc reply of ", #res, " bytes")
+        sock:close()
+        print("ssl client hello: UDP cosocket test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl client hello: UDP cosocket test start|ssl client hello: received UDP memc reply of \d+ bytes|ssl client hello: UDP cosocket test done|test completed)/
+--- grep_error_log_out
+ssl client hello: UDP cosocket test start
+ssl client hello: received UDP memc reply of 12 bytes
+ssl client hello: UDP cosocket test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 34: ssl_client_hello_by_lua* with ngx.timer (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("ssl client hello: timer test start")
+
+        local function timer_handler()
+            print("ssl client hello: timer executed")
+        end
+
+        local ok, err = ngx.timer.at(0, timer_handler)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to create timer: ", err)
+            return
+        end
+
+        print("ssl client hello: timer created")
+        print("ssl client hello: timer test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl client hello: timer test start|ssl client hello: timer created|ssl client hello: timer test done|ssl client hello: timer executed|test completed)/
+--- grep_error_log_out
+ssl client hello: timer test start
+ssl client hello: timer created
+ssl client hello: timer test done
+ssl client hello: timer executed
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 35: ssl_client_hello_by_lua* with user threads (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("ssl client hello: uthread test start")
+
+        local function worker()
+            ngx.sleep(0.01)
+            print("ssl client hello: uthread worker executed")
+            return "worker_result"
+        end
+
+        local t, err = ngx.thread.spawn(worker)
+        if not t then
+            ngx.log(ngx.ERR, "failed to spawn thread: ", err)
+            return
+        end
+
+        print("ssl client hello: uthread spawned")
+
+        local ok, res = ngx.thread.wait(t)
+        if not ok then
+            ngx.log(ngx.ERR, "failed to wait thread: ", res)
+            return
+        end
+
+        print("ssl client hello: uthread result: ", res)
+        print("ssl client hello: uthread test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl client hello: uthread test start|ssl client hello: uthread spawned|ssl client hello: uthread worker executed|ssl client hello: uthread result: worker_result|ssl client hello: uthread test done|test completed)/
+--- grep_error_log_out
+ssl client hello: uthread test start
+ssl client hello: uthread spawned
+ssl client hello: uthread worker executed
+ssl client hello: uthread result: worker_result
+ssl client hello: uthread test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 36: ssl_client_hello_by_lua* with coroutines (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("ssl client hello: coroutine test start")
+
+        local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield
+
+        local function coro_func()
+            local cnt = 0
+            for i = 1, 3 do
+                print("ssl client hello: coro yield: ", cnt)
+                cy()
+                cnt = cnt + 1
+            end
+            return "coro_done"
+        end
+
+        local c = cc(coro_func)
+        for i = 1, 4 do
+            print("ssl client hello: coro resume, status: ", coroutine.status(c))
+            local ok, res = cr(c)
+            if not ok then
+                print("ssl client hello: coro error: ", res)
+                break
+            end
+            if coroutine.status(c) == "dead" then
+                print("ssl client hello: coro result: ", res)
+                break
+            end
+        end
+
+        print("ssl client hello: coroutine test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl client hello: coroutine test start|ssl client hello: coro resume, status: \w+|ssl client hello: coro yield: \d+|ssl client hello: coro result: coro_done|ssl client hello: coroutine test done|test completed)/
+--- grep_error_log_out
+ssl client hello: coroutine test start
+ssl client hello: coro resume, status: suspended
+ssl client hello: coro yield: 0
+ssl client hello: coro resume, status: suspended
+ssl client hello: coro yield: 1
+ssl client hello: coro resume, status: suspended
+ssl client hello: coro yield: 2
+ssl client hello: coro resume, status: suspended
+ssl client hello: coro result: coro_done
+ssl client hello: coroutine test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 37: ssl_client_hello_by_lua* without yield API (simple logic)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("ssl client hello: simple test start")
+
+        -- Simple calculations without yield
+        local sum = 0
+        for i = 1, 10 do
+            sum = sum + i
+        end
+
+        print("ssl client hello: calculated sum: ", sum)
+
+        -- String operations
+        local str = "hello"
+        str = str .. " world"
+        print("ssl client hello: concatenated string: ", str)
+
+        -- Table operations
+        local t = {a = 1, b = 2, c = 3}
+        local count = 0
+        for k, v in pairs(t) do
+            count = count + v
+        end
+        print("ssl client hello: table sum: ", count)
+
+        print("ssl client hello: simple test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl client hello: simple test start|ssl client hello: calculated sum: 55|ssl client hello: concatenated string: hello world|ssl client hello: table sum: 6|ssl client hello: simple test done|test completed)/
+--- grep_error_log_out
+ssl client hello: simple test start
+ssl client hello: calculated sum: 55
+ssl client hello: concatenated string: hello world
+ssl client hello: table sum: 6
+ssl client hello: simple test done
+test completed
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 38: ssl_client_hello_by_lua* with multiple network operations (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("ssl client hello: multiple network test start")
+
+        -- First TCP operation
+        local sock1 = ngx.socket.tcp()
+        sock1:settimeout(2000)
+        local ok, err = sock1:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if ok then
+            local bytes, err = sock1:send("version\r\n")
+            if bytes then
+                local res, err = sock1:receive()
+                if res then
+                    print("ssl client hello: TCP1 version: ", res)
+                end
+            end
+            sock1:close()
+        end
+
+        ngx.sleep(0.01)  -- Small delay
+
+        -- Second UDP operation
+        local sock2 = ngx.socket.udp()
+        sock2:settimeout(1000)
+        local ok, err = sock2:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if ok then
+            local req = "\0\1\0\0\0\1\0\0version\r\n"
+            local ok, err = sock2:send(req)
+            if ok then
+                local res, err = sock2:receive()
+                if res then
+                    print("ssl client hello: UDP version reply length: ", #res)
+                end
+            end
+            sock2:close()
+        end
+
+        print("ssl client hello: multiple network test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(ssl client hello: multiple network test start|ssl client hello: TCP1 version: VERSION|ssl client hello: UDP version reply length: \d+|ssl client hello: multiple network test done|test completed)/
+--- grep_error_log_out eval
+[
+qr/ssl client hello: multiple network test start/,
+qr/ssl client hello: TCP1 version: VERSION/,
+qr/ssl client hello: UDP version reply length: \d+/,
+qr/ssl client hello: multiple network test done/,
+qr/test completed/,
+]
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 39: ssl_cert_by_lua* and ssl_client_hello_by_lua* with sleep (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- http_config
+    ssl_certificate_by_lua_block {
+        print("cert by: starting with sleep")
+        local begin = ngx.now()
+        ngx.sleep(0.05)
+        print("cert by: slept for ", ngx.now() - begin, " seconds")
+    }
+
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("client hello: starting with sleep")
+        local begin = ngx.now()
+        ngx.sleep(0.1)
+        print("client hello: slept for ", ngx.now() - begin, " seconds")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(client hello: starting with sleep|client hello: slept for 0\.\d+|cert by: starting with sleep|cert by: slept for 0\.\d+|test completed)/
+--- grep_error_log_out eval
+[
+qr/client hello: starting with sleep/,
+qr/client hello: slept for 0\.(?:09|1\d)\d+/,
+qr/cert by: starting with sleep/,
+qr/cert by: slept for 0\.0[4-6]\d+/,
+qr/test completed/,
+]
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 40: ssl_cert_by_lua* and ssl_client_hello_by_lua* with cosocket (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- http_config
+    ssl_certificate_by_lua_block {
+        print("cert by: cosocket test start")
+
+        local sock = ngx.socket.udp()
+        sock:settimeout(1000)
+
+        local ok, err = sock:setpeername("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if not ok then
+            ngx.log(ngx.ERR, "cert by: failed to connect to memc: ", err)
+            return
+        end
+
+        local req = "\0\1\0\0\0\1\0\0flush_all\r\n"
+        local ok, err = sock:send(req)
+        if not ok then
+            ngx.log(ngx.ERR, "cert by: failed to send flush_all to memc: ", err)
+            return
+        end
+
+        local res, err = sock:receive()
+        if not res then
+            ngx.log(ngx.ERR, "cert by: failed to receive memc reply: ", err)
+            return
+        end
+
+        print("cert by: received UDP memc reply of ", #res, " bytes")
+        sock:close()
+        print("cert by: cosocket test done")
+    }
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("client hello: cosocket test start")
+
+        local sock = ngx.socket.tcp()
+        sock:settimeout(2000)
+
+        local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if not ok then
+            ngx.log(ngx.ERR, "client hello: failed to connect to memc: ", err)
+            return
+        end
+
+        local bytes, err = sock:send("version\r\n")
+        if not bytes then
+            ngx.log(ngx.ERR, "client hello: failed to send version command: ", err)
+            return
+        end
+
+        local res, err = sock:receive()
+        if not res then
+            ngx.log(ngx.ERR, "client hello: failed to receive memc reply: ", err)
+            return
+        end
+
+        print("client hello: received memc reply: ", res)
+        sock:close()
+        print("client hello: cosocket test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(client hello: cosocket test start|client hello: received memc reply: VERSION|client hello: cosocket test done|cert by: cosocket test start|cert by: received UDP memc reply of \d+ bytes|cert by: cosocket test done|test completed)/
+--- grep_error_log_out eval
+[
+qr/client hello: cosocket test start/,
+qr/client hello: received memc reply: VERSION/,
+qr/client hello: cosocket test done/,
+qr/cert by: cosocket test start/,
+qr/cert by: received UDP memc reply of \d+ bytes/,
+qr/cert by: cosocket test done/,
+qr/test completed/,
+]
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 41: ssl_cert_by_lua* and ssl_client_hello_by_lua* with timer and uthread (yield API)
+--- skip_eval: 6:!$ENV{TEST_NGINX_USE_HTTP3}
+--- http_config
+    ssl_certificate_by_lua_block {
+        print("cert by: coroutine test start")
+
+        local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield
+
+        local function coro_func()
+            local cnt = 0
+            for i = 1, 2 do
+                print("cert by: coro yield: ", cnt)
+                cy()
+                cnt = cnt + 1
+            end
+            return "cert_by_coro_done"
+        end
+
+        local c = cc(coro_func)
+        for i = 1, 3 do
+            print("cert by: coro resume, status: ", coroutine.status(c))
+            local ok, res = cr(c)
+            if not ok then
+                print("cert by: coro error: ", res)
+                break
+            end
+            if coroutine.status(c) == "dead" then
+                print("cert by: coro result: ", res)
+                break
+            end
+        end
+
+        -- Small sleep to allow timer to execute
+        ngx.sleep(0.01)
+        print("cert by: coroutine test done")
+    }
+
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("client hello: timer and uthread test start")
+
+        -- Timer test
+        local function timer_handler()
+            print("client hello: timer executed")
+        end
+
+        local ok, err = ngx.timer.at(0, timer_handler)
+        if not ok then
+            ngx.log(ngx.ERR, "client hello: failed to create timer: ", err)
+            return
+        end
+
+        print("client hello: timer created")
+
+        -- User thread test
+        local function worker()
+            ngx.sleep(0.01)
+            print("client hello: uthread worker executed")
+            return "client_hello_result"
+        end
+
+        local t, err = ngx.thread.spawn(worker)
+        if not t then
+            ngx.log(ngx.ERR, "client hello: failed to spawn thread: ", err)
+            return
+        end
+
+        print("client hello: uthread spawned")
+
+        local ok, res = ngx.thread.wait(t)
+        if not ok then
+            ngx.log(ngx.ERR, "client hello: failed to wait thread: ", res)
+            return
+        end
+
+        print("client hello: uthread result: ", res)
+        print("client hello: timer and uthread test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- response_body
+test completed
+
+--- grep_error_log eval: qr/(client hello: timer and uthread test start|client hello: timer created|client hello: uthread spawned|client hello: uthread worker executed|client hello: uthread result: client_hello_result|client hello: timer and uthread test done|cert by: coroutine test start|cert by: coro resume, status: \w+|cert by: coro yield: \d+|cert by: coro result: cert_by_coro_done|cert by: coroutine test done|client hello: timer executed|test completed)/
+--- grep_error_log_out eval
+[
+qr/client hello: timer and uthread test start/,
+qr/client hello: timer created/,
+qr/client hello: uthread spawned/,
+qr/client hello: uthread worker executed/,
+qr/client hello: uthread result: client_hello_result/,
+qr/client hello: timer and uthread test done/,
+qr/cert by: coroutine test start/,
+qr/cert by: coro resume, status: suspended/,
+qr/cert by: coro yield: 0/,
+qr/cert by: coro resume, status: suspended/,
+qr/cert by: coro yield: 1/,
+qr/cert by: coro resume, status: suspended/,
+qr/cert by: coro result: cert_by_coro_done/,
+qr/cert by: coroutine test done/,
+qr/client hello: timer executed/,
+qr/test completed/,
+]
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 42: ssl_cert_by_lua* with cosocket (yield API) and ssl_client_hello_by_lua* failure
+--- skip_eval: 8:!$ENV{TEST_NGINX_USE_HTTP3}
+--- http_config
+    ssl_certificate_by_lua_block {
+        print("cert by: test start")
+        ngx.exit(500)
+    }
+--- config
+    server_tokens off;
+    lua_ssl_trusted_certificate ../../cert/test.crt;
+    lua_ssl_verify_depth 3;
+
+    ssl_client_hello_by_lua_block {
+        print("client hello: cosocket test start")
+
+        local sock = ngx.socket.tcp()
+        sock:settimeout(2000)
+
+        local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_MEMCACHED_PORT)
+        if not ok then
+            ngx.log(ngx.ERR, "client hello: failed to connect to memc: ", err)
+            return
+        end
+
+        local bytes, err = sock:send("version\r\n")
+        if not bytes then
+            ngx.log(ngx.ERR, "client hello: failed to send version command: ", err)
+            return
+        end
+
+        local res, err = sock:receive()
+        if not res then
+            ngx.log(ngx.ERR, "client hello: failed to receive memc reply: ", err)
+            return
+        end
+
+        print("client hello: received memc reply: ", res)
+        sock:close()
+        print("client hello: cosocket test done")
+    }
+
+    location /t {
+        content_by_lua_block {
+            print("test completed")
+            ngx.say("test completed")
+        }
+    }
+--- request
+GET /t
+--- ignore_response
+--- curl_error eval
+qr/Connection time/
+--- grep_error_log eval: qr/(client hello: cosocket test start|client hello: received memc reply: VERSION|client hello: cosocket test done|cert by: test start)/
+--- grep_error_log_out eval
+[
+qr/client hello: cosocket test start/,
+qr/client hello: received memc reply: VERSION/,
+qr/client hello: cosocket test done/,
+qr/cert by: test start/,
+]
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
diff -pruN 1:0.10.28-3/t/167-server-rewrite.t 1:0.10.29-1/t/167-server-rewrite.t
--- 1:0.10.28-3/t/167-server-rewrite.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/167-server-rewrite.t	2025-12-07 15:22:42.000000000 +0000
@@ -468,6 +468,7 @@ GET /lua
 failed to load inlined Lua code: server_rewrite_by_lua(nginx.conf:25):2: unexpected symbol near ''for end''
 --- no_error_log
 no_such_error
+--- skip_eval: 2:$ENV{TEST_NGINX_USE_HUP}
 
 
 
diff -pruN 1:0.10.28-3/t/168-tcp-socket-bind.t 1:0.10.29-1/t/168-tcp-socket-bind.t
--- 1:0.10.28-3/t/168-tcp-socket-bind.t	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/168-tcp-socket-bind.t	2025-12-07 15:22:42.000000000 +0000
@@ -365,3 +365,64 @@ GET /t
 connected: 1
 --- no_error_log
 [error]
+
+
+
+=== TEST 7: upstream sockets bind with ip port
+--- config
+   server_tokens off;
+   location /t {
+        set $port $TEST_NGINX_SERVER_PORT;
+        content_by_lua_block {
+            local ip = "127.0.0.1"
+            local port = ngx.var.port
+
+            local sock = ngx.socket.tcp()
+
+            local ok, err = sock:bind(ip, 12345)
+            if not ok then
+                ngx.say("failed to bind", err)
+                return
+            end
+  
+            local ok, err = sock:connect("127.0.0.1", port)
+            if not ok then
+                ngx.say("failed to connect: ", err)
+                return
+            end
+
+            local ok, err = sock:setoption("reuseaddr", 1)
+            if not ok then
+                ngx.say("setoption reuseaddr failed: ", err)
+            end                  
+
+            ngx.say("connected: ", ok)
+
+            local bytes, err = sock:send("GET /foo HTTP/1.1\r\nHost: localhost\r\nConnection: keepalive\r\n\r\n")
+            if not bytes then
+                ngx.say("failed to send request: ", err)
+                return
+            end
+
+            local reader = sock:receiveuntil("\r\n0\r\n\r\n")
+            local data, err = reader()
+
+            if not data then
+                ngx.say("failed to receive response body: ", err)
+                return
+            end
+            sock:close()
+            ngx.say(data)
+
+        }
+    }
+
+    location /foo {
+        echo bind: $remote_addr:$remote_port;
+    }
+--- request
+GET /t
+--- response_body eval
+qr/bind:\s127\.0\.0\.1:12345|failed\s+to\s+connect:\s+address\s+already\s+in\s+use/
+--- error_log eval
+"lua tcp socket bind ip: 127.0.0.1"
diff -pruN 1:0.10.28-3/t/169-proxy-ssl-verify.t 1:0.10.29-1/t/169-proxy-ssl-verify.t
--- 1:0.10.28-3/t/169-proxy-ssl-verify.t	1970-01-01 00:00:00.000000000 +0000
+++ 1:0.10.29-1/t/169-proxy-ssl-verify.t	2025-12-07 15:22:42.000000000 +0000
@@ -0,0 +1,1337 @@
+# vim:set ft= ts=4 sw=4 et fdm=marker:
+
+use Test::Nginx::Socket::Lua;
+
+repeat_each(3);
+
+# All these tests need to have new openssl
+my $NginxBinary = $ENV{'TEST_NGINX_BINARY'} || 'nginx';
+my $openssl_version = eval { `$NginxBinary -V 2>&1` };
+
+if ($openssl_version =~ m/built with OpenSSL (\d+)\.(\d+)\.(\d+)/) {
+    my ($major, $minor, $patch) = ($1, $2, $3);
+
+    if ($major < 3 || ($major == 3 && $minor == 0 && $patch < 2)) {
+        plan(skip_all => "too old OpenSSL, need >= 3.0.2, was " .
+            "$major.$minor.$patch");
+    } else {
+        plan tests => repeat_each() * (blocks() * 5 + 19);
+    }
+} elsif ($openssl_version =~ m/running with BoringSSL/) {
+    plan(skip_all => "does not support BoringSSL");
+} else {
+    die "unknown SSL";
+}
+
+$ENV{TEST_NGINX_HTML_DIR} ||= html_dir();
+$ENV{TEST_NGINX_MEMCACHED_PORT} ||= 11211;
+$ENV{TEST_NGINX_QUIC_IDLE_TIMEOUT} ||= 0.6;
+
+#log_level 'warn';
+log_level 'debug';
+
+no_long_string();
+#no_diff();
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: invalid proxy_pass url
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("hello world")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.log(ngx.INFO, "hello world")
+        }
+    }
+--- request
+GET /t
+--- error_log
+proxy_ssl_verify_by_lua* should be used with proxy_pass https url
+--- must_die
+
+
+
+=== TEST 2: proxy_ssl_verify_by_lua in http {} block
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("hello world")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+
+    proxy_ssl_verify_by_lua_block {
+        ngx.log(ngx.INFO, "hello world")
+    }
+--- config
+    location /t {
+        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+    }
+--- request
+GET /t
+--- error_log
+"proxy_ssl_verify_by_lua_block" directive is not allowed here
+--- must_die
+
+
+
+=== TEST 3: proxy_ssl_verify_by_lua in server {} block
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("hello world")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+
+--- config
+    proxy_ssl_verify_by_lua_block {
+        ngx.log(ngx.INFO, "hello world")
+    }
+
+    location /t {
+        proxy_pass                  http://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+    }
+--- request
+GET /t
+--- error_log
+"proxy_ssl_verify_by_lua_block" directive is not allowed here
+--- must_die
+
+
+
+=== TEST 4: simple logging
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+        server_name   test.com;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("simple logging return")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.log(ngx.INFO, "proxy ssl verify by lua is running!")
+        }
+    }
+--- request
+GET /t
+--- response_body
+simple logging return
+--- error_log
+proxy ssl verify by lua is running!
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 5: sleep
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("sleep")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+
+        proxy_ssl_verify_by_lua_block {
+            local begin = ngx.now()
+            ngx.sleep(0.1)
+            print("elapsed in proxy ssl verify by lua: ", ngx.now() - begin)
+        }
+    }
+--- request
+GET /t
+--- response_body
+sleep
+--- error_log eval
+qr/elapsed in proxy ssl verify by lua: 0.(?:09|1\d)\d+ while loading proxy ssl verify by lua,/,
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 6: timer
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("timer")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+
+        proxy_ssl_verify_by_lua_block {
+            local function f()
+                print("my timer run!")
+            end
+            local ok, err = ngx.timer.at(0, f)
+            if not ok then
+                ngx.log(ngx.ERR, "failed to create timer: ", err)
+                return
+            end
+        }
+    }
+--- request
+GET /t
+--- response_body
+timer
+--- error_log
+my timer run!
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 7: ngx.exit(0) - no yield
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("ngx.exit(0) no yield")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.exit(0)
+            ngx.log(ngx.ERR, "should never reached here...")
+        }
+    }
+--- request
+GET /t
+--- response_body
+ngx.exit(0) no yield
+--- error_log
+lua exit with code 0
+--- no_error_log
+should never reached here
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 8: ngx.exit(ngx.ERROR) - no yield
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("ngx.exit(ngx.ERROR) no yield")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.exit(ngx.ERROR)
+            ngx.log(ngx.ERR, "should never reached here...")
+        }
+    }
+--- request
+GET /t
+--- error_code: 502
+--- error_log eval
+[
+'lua exit with code -1',
+'proxy_ssl_verify_by_lua: handler return value: -1, cert verify callback exit code: 0',
+qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/,
+]
+--- no_error_log
+should never reached here
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 9: ngx.exit(0) -  yield
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("ngx.exit(0) yield")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.sleep(0.001)
+            ngx.exit(0)
+
+            ngx.log(ngx.ERR, "should never reached here...")
+        }
+    }
+--- request
+GET /t
+--- response_body
+ngx.exit(0) yield
+--- error_log
+lua exit with code 0
+--- no_error_log
+should never reached here
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 10: ngx.exit(ngx.ERROR) - yield
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("ngx.exit(ngx.ERROR) yield")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.sleep(0.001)
+            ngx.exit(ngx.ERROR)
+
+            ngx.log(ngx.ERR, "should never reached here...")
+        }
+    }
+--- request
+GET /t
+--- error_code: 502
+--- error_log eval
+[
+'lua exit with code -1',
+'proxy_ssl_verify_by_lua: cert verify callback exit code: 0',
+qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/,
+]
+--- no_error_log
+should never reached here
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 11: lua exception - no yield
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("lua exception - no yield")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            error("bad bad bad")
+            ngx.log(ngx.ERR, "should never reached here...")
+        }
+    }
+--- request
+GET /t
+--- error_code: 502
+--- error_log eval
+[
+'runtime error: proxy_ssl_verify_by_lua(nginx.conf:65):2: bad bad bad',
+'proxy_ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0',
+qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/,
+]
+--- no_error_log
+should never reached here
+[alert]
+[emerg]
+
+
+
+=== TEST 12: lua exception - yield
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("lua exception - yield")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.sleep(0.001)
+            error("bad bad bad")
+            ngx.log(ngx.ERR, "should never reached here...")
+        }
+    }
+--- request
+GET /t
+--- error_code: 502
+--- error_log eval
+[
+'runtime error: proxy_ssl_verify_by_lua(nginx.conf:65):3: bad bad bad',
+'proxy_ssl_verify_by_lua: cert verify callback exit code: 0',
+qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/,
+]
+--- no_error_log
+should never reached here
+[alert]
+[emerg]
+
+
+
+=== TEST 13: get phase
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("get phase return")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            print("get_phase: ", ngx.get_phase())
+        }
+    }
+--- request
+GET /t
+--- response_body
+get phase return
+--- error_log
+get_phase: proxy_ssl_verify
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 14: subrequests disabled
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("subrequests disabled")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.location.capture("/foo")
+        }
+    }
+--- request
+GET /t
+--- error_code: 502
+--- error_log eval
+[
+'proxy_ssl_verify_by_lua(nginx.conf:65):2: API disabled in the context of proxy_ssl_verify_by_lua*',
+'proxy_ssl_verify_by_lua: handler return value: 500, cert verify callback exit code: 0',
+qr/.*? SSL_do_handshake\(\) failed .*?certificate verify failed/,
+]
+--- no_error_log
+[alert]
+
+
+
+=== TEST 15: simple logging (by_lua_file)
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("simple logging by lua file")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- user_files
+>>> a.lua
+print("proxy ssl verify by lua is running!")
+
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_file html/a.lua;
+    }
+--- request
+GET /t
+--- response_body
+simple logging by lua file
+--- error_log
+a.lua:1: proxy ssl verify by lua is running!
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 16: coroutine API
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("coroutine API")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            local cc, cr, cy = coroutine.create, coroutine.resume, coroutine.yield
+
+            local function f()
+                local cnt = 0
+                for i = 1, 20 do
+                    print("co yield: ", cnt)
+                    cy()
+                    cnt = cnt + 1
+                end
+            end
+
+            local c = cc(f)
+            for i = 1, 3 do
+                print("co resume, status: ", coroutine.status(c))
+                cr(c)
+            end
+        }
+    }
+--- request
+GET /t
+--- response_body
+coroutine API
+--- grep_error_log eval: qr/co (?:yield: \d+|resume, status: \w+)/
+--- grep_error_log_out
+co resume, status: suspended
+co yield: 0
+co resume, status: suspended
+co yield: 1
+co resume, status: suspended
+co yield: 2
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 17: simple user thread wait with yielding
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("simple user thread wait with yielding")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            local function f()
+                ngx.sleep(0.01)
+                print("uthread: hello in thread")
+                return "done"
+            end
+
+            local t, err = ngx.thread.spawn(f)
+            if not t then
+                ngx.log(ngx.ERR, "uthread: failed to spawn thread: ", err)
+                return ngx.exit(ngx.ERROR)
+            end
+
+            print("uthread: thread created: ", coroutine.status(t))
+
+            local ok, res = ngx.thread.wait(t)
+            if not ok then
+                print("uthread: failed to wait thread: ", res)
+                return
+            end
+
+            print("uthread: ", res)
+        }
+    }
+--- request
+GET /t
+--- response_body
+simple user thread wait with yielding
+--- no_error_log
+[error]
+[alert]
+--- grep_error_log eval: qr/uthread: [^.,]+/
+--- grep_error_log_out
+uthread: thread created: running while loading proxy ssl verify by lua
+uthread: hello in thread while loading proxy ssl verify by lua
+uthread: done while loading proxy ssl verify by lua
+
+
+
+=== TEST 18: uthread (kill)
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("uthread (kill)")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            local function f()
+                ngx.log(ngx.INFO, "uthread: hello from f()")
+                ngx.sleep(1)
+            end
+
+            local t, err = ngx.thread.spawn(f)
+            if not t then
+                ngx.log(ngx.ERR, "failed to spawn thread: ", err)
+                return ngx.exit(ngx.ERROR)
+            end
+
+            local ok, res = ngx.thread.kill(t)
+            if not ok then
+                ngx.log(ngx.ERR, "failed to kill thread: ", res)
+                return
+            end
+
+            ngx.log(ngx.INFO, "uthread: killed")
+
+            local ok, err = ngx.thread.kill(t)
+            if not ok then
+                ngx.log(ngx.INFO, "uthread: failed to kill: ", err)
+            end
+        }
+    }
+--- request
+GET /t
+--- response_body
+uthread (kill)
+--- no_error_log
+[error]
+[alert]
+[emerg]
+--- grep_error_log eval: qr/uthread: [^.,]+/
+--- grep_error_log_out
+uthread: hello from f() while loading proxy ssl verify by lua
+uthread: killed while loading proxy ssl verify by lua
+uthread: failed to kill: already waited or killed while loading proxy ssl verify by lua
+
+
+
+=== TEST 19: ngx.exit(ngx.OK) - no yield
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("ngx.exit(ngx.OK) - no yield")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.exit(ngx.OK)
+            ngx.log(ngx.ERR, "should never reached here...")
+        }
+    }
+--- request
+GET /t
+--- response_body
+ngx.exit(ngx.OK) - no yield
+--- error_log eval
+[
+'proxy_ssl_verify_by_lua: handler return value: 0, cert verify callback exit code: 1',
+qr/\[debug\] .*? SSL_do_handshake: 1/,
+'lua exit with code 0',
+]
+--- no_error_log
+should never reached here
+[alert]
+[emerg]
+
+
+
+=== TEST 20: proxy_ssl_verify_by_lua* without yield API (simple logic)
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("without yield API, simple logic")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            print("proxy ssl verify: simple test start")
+
+            -- Simple calculations without yield
+            local sum = 0
+            for i = 1, 10 do
+                sum = sum + i
+            end
+
+            print("proxy ssl verify: calculated sum: ", sum)
+
+            -- String operations
+            local str = "hello"
+            str = str .. " world"
+            print("proxy ssl verify: concatenated string: ", str)
+
+            -- Table operations
+            local t = {a = 1, b = 2, c = 3}
+            local count = 0
+            for k, v in pairs(t) do
+                count = count + v
+            end
+            print("proxy ssl verify: table sum: ", count)
+
+            print("proxy ssl verify: simple test done")
+        }
+    }
+--- request
+GET /t
+--- response_body
+without yield API, simple logic
+--- grep_error_log eval: qr/(proxy ssl verify: simple test start|proxy ssl verify: calculated sum: 55|proxy ssl verify: concatenated string: hello world|proxy ssl verify: table sum: 6|proxy ssl verify: simple test done)/
+--- grep_error_log_out
+proxy ssl verify: simple test start
+proxy ssl verify: calculated sum: 55
+proxy ssl verify: concatenated string: hello world
+proxy ssl verify: table sum: 6
+proxy ssl verify: simple test done
+
+--- no_error_log
+[error]
+[alert]
+[emerg]
+
+
+
+=== TEST 21: lua_upstream_skip_openssl_default_verify default off
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("lua_upstream_skip_openssl_default_verify default off")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.log(ngx.INFO, "proxy ssl verify by lua is running!")
+        }
+    }
+--- request
+GET /t
+--- error_log
+proxy_ssl_verify_by_lua: openssl default verify
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 22: lua_upstream_skip_openssl_default_verify on
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("lua_upstream_skip_openssl_default_verify default off")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_ssl_conf_command        VerifyMode Peer;
+
+        lua_upstream_skip_openssl_default_verify on;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.log(ngx.INFO, "proxy ssl verify by lua is running!")
+        }
+    }
+--- request
+GET /t
+--- response_body
+lua_upstream_skip_openssl_default_verify default off
+--- error_log
+proxy ssl verify by lua is running!
+--- no_error_log
+proxy_ssl_verify_by_lua: openssl default verify
+[error]
+[alert]
+
+
+
+=== TEST 23: ngx.ctx to pass data from downstream phase to upstream phase
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+        server_name   test.com;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("simple logging return")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+
+        rewrite_by_lua_block {
+            ngx.ctx.greeting = "I am from rewrite phase"
+        }
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.log(ngx.INFO, "greeting: ", ngx.ctx.greeting)
+        }
+    }
+--- request
+GET /t
+--- response_body
+simple logging return
+--- error_log
+greeting: I am from rewrite phase
+--- no_error_log
+[error]
+[alert]
+
+
+
+=== TEST 24: upstream connection aborted
+--- http_config
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+        server_name   test.com;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("hello world")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+        proxy_connect_timeout         100ms;
+
+        proxy_ssl_verify_by_lua_block {
+            ngx.sleep(0.2)
+        }
+    }
+--- request
+GET /t
+--- error_code: 504
+--- response_body_like: 504 Gateway Time-out
+--- error_log
+upstream timed out (110: Connection timed out) while loading proxy ssl verify by lua
+proxy_ssl_verify_by_lua: cert verify callback aborted
+--- no_error_log
+[alert]
+--- wait: 0.5
+
+
+
+=== TEST 25: cosocket
+--- http_config
+    server {
+        listen 127.0.0.1:$TEST_NGINX_RAND_PORT_1;
+        server_name test.com;
+
+        server_tokens off;
+        location /foo {
+            default_type 'text/plain';
+            content_by_lua_block {
+                ngx.sleep(0.1)
+
+                ngx.status = 201
+                ngx.say("foo")
+                ngx.exit(201)
+            }
+            more_clear_headers Date;
+        }
+    }
+
+    server {
+        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl;
+        server_name   test.com;
+
+        ssl_certificate ../../cert/mtls_server.crt;
+        ssl_certificate_key ../../cert/mtls_server.key;
+
+        location / {
+            default_type 'text/plain';
+
+            content_by_lua_block {
+                ngx.say("simple logging return")
+            }
+
+            more_clear_headers Date;
+        }
+    }
+--- config
+    location /t {
+        proxy_pass                    https://unix:$TEST_NGINX_HTML_DIR/nginx.sock;
+        proxy_ssl_verify              on;
+        proxy_ssl_name                example.com;
+        proxy_ssl_certificate         ../../cert/mtls_client.crt;
+        proxy_ssl_certificate_key     ../../cert/mtls_client.key;
+        proxy_ssl_trusted_certificate ../../cert/mtls_ca.crt;
+        proxy_ssl_session_reuse       off;
+
+        proxy_ssl_verify_by_lua_block {
+            do
+                local sock = ngx.socket.tcp()
+                sock:settimeout(2000)
+
+                local ok, err = sock:connect("127.0.0.1", $TEST_NGINX_RAND_PORT_1)
+                if not ok then
+                    ngx.log(ngx.ERR, "failed to connect: ", err)
+                    return
+                end
+
+                ngx.log(ngx.INFO, "connected: ", ok)
+
+                local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n"
+                local bytes, err = sock:send(req)
+                if not bytes then
+                    ngx.log(ngx.ERR, "failed to send http request: ", err)
+                    return
+                end
+
+                ngx.log(ngx.INFO, "sent http request: ", bytes, " bytes.")
+
+                while true do
+                    local line, err = sock:receive()
+                    if not line then
+                        -- ngx.log(ngx.ERR, "failed to receive response status line: ", err)
+                        break
+                    end
+
+                    ngx.log(ngx.INFO, "received: ", line)
+                end
+
+                local ok, err = sock:close()
+                ngx.log(ngx.INFO, "close: ", ok, " ", err)
+            end -- do
+            -- collectgarbage()
+        }
+    }
+--- request
+GET /t
+--- response_body
+simple logging return
+--- error_log
+connected: 1
+sent http request: 56 bytes.
+received: HTTP/1.1 201 Created
+received: Server: nginx
+received: Content-Type: text/plain
+received: Content-Length: 4
+received: Connection: close
+received:
+received: foo
+close: 1 nil
+--- no_error_log
+[error]
+[alert]
diff -pruN 1:0.10.28-3/t/cert/gen-test-passphrase.sh 1:0.10.29-1/t/cert/gen-test-passphrase.sh
--- 1:0.10.28-3/t/cert/gen-test-passphrase.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1:0.10.29-1/t/cert/gen-test-passphrase.sh	2025-12-07 15:22:42.000000000 +0000
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# Set variables
+SUBJECT="/C=CN/ST=Guangdong/L=ShenZhen/O=OpenResty/OU=OpenResty/CN=test.com/emailAddress=guanglinlv@gmail.com"
+DAYS=49000  # Approximately 134 years
+KEY_FILE="test_passphrase.key"
+CERT_FILE="test_passphrase.crt"
+PASSWORD="123456"
+
+# Generate a new 2048-bit RSA private key, encrypted with the password
+openssl genrsa -aes256 -passout pass:$PASSWORD -out $KEY_FILE 2048
+
+# Generate a new self-signed certificate
+openssl req -x509 -new -nodes -key $KEY_FILE -sha256 -days $DAYS \
+    -out $CERT_FILE -subj "$SUBJECT" -passin pass:$PASSWORD
+
+# Display information about the new certificate
+openssl x509 -in $CERT_FILE -text -noout
+
+echo "New 2048-bit certificate generated successfully!"
+echo "Private key (encrypted): $KEY_FILE"
+echo "Certificate: $CERT_FILE"
+
diff -pruN 1:0.10.28-3/t/cert/gen-test2.sh 1:0.10.29-1/t/cert/gen-test2.sh
--- 1:0.10.28-3/t/cert/gen-test2.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1:0.10.29-1/t/cert/gen-test2.sh	2025-12-07 15:22:42.000000000 +0000
@@ -0,0 +1,3 @@
+openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \
+    -subj "/C=US/ST=California/L=San Francisco/O=OpenResty/CN=test2.com/emailAddress=openresty@gmail.com" \
+    -keyout test2.key -out test2.crt
diff -pruN 1:0.10.28-3/t/cert/test2.crt 1:0.10.29-1/t/cert/test2.crt
--- 1:0.10.28-3/t/cert/test2.crt	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/cert/test2.crt	2025-12-07 15:22:42.000000000 +0000
@@ -1,16 +1,24 @@
 -----BEGIN CERTIFICATE-----
-MIIChzCCAfACCQDjCkJpJUtZmjANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMC
-VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x
-EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZI
-hvcNAQkBFhNvcGVucmVzdHlAZ21haWwuY29tMCAXDTE0MDkxMzAwMTgxMFoYDzIx
-MTQwODIwMDAxODEwWjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju
-aWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU9wZW5SZXN0eTES
-MBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZIhvcNAQkBFhNvcGVucmVzdHlAZ21h
-aWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDy+OVI2u5NBOeB2Cyz
-Gnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7f
-JrKFziJxMy4g4Kdn9G659vE7CWu/UAVjRUtc+mTBAEfjdbumizmHLG7DmnNhGl3R
-NGiVNLsUInSMGfUlJRzZJXhI4QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAEMmRvyN
-N7uE24Tc6TR19JadNHK8g3YGktRoXWiqd/y0HY4NRPgvnK/nX7CY/wXa1j+uDO8K
-e6/Ldm5RZrjtvfHJmTSAu8zkqTJz8bqRDH7kzL5Ni2Ky2x8r9dtB0ImpOiSlwvZN
-snMvbrxEdwBiqlC9prV2f9aG+ACo1KnPL0j6
+MIID7zCCAtegAwIBAgIUS1f/CoGJaRaJvBgrXEXeTSm7kM4wDQYJKoZIhvcNAQEL
+BQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAMMCXRl
+c3QyLmNvbTEiMCAGCSqGSIb3DQEJARYTb3BlbnJlc3R5QGdtYWlsLmNvbTAeFw0y
+NDAzMjQxMDI1NDhaFw0zNDAzMjIxMDI1NDhaMIGGMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAGA1UE
+CgwJT3BlblJlc3R5MRIwEAYDVQQDDAl0ZXN0Mi5jb20xIjAgBgkqhkiG9w0BCQEW
+E29wZW5yZXN0eUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQChXBMRP5X9Y0l5NogyIZatou1YDT2BDE9tza6dYKUdraJlGo1T4DZf7LoD
+mKFvIpVqLedppZ9z9SrlrsZ8Be6OUbw3kxSyUPDvpauI4XXBR2ZuAHIzTy/Uare1
+1iZSjbT0KATiWviMWCWWblyBU5QPMFJJOmcjRDAZocfIUs4CzglRaDorl5qUFJHr
+mROaIsag/10nnClTwaGwj1pHmFitKRHTIrYICYd5wGgstte9mDFz15s9a3f/Dwhc
+/7neEHYaryRVhR627aDGVHHpcMRWr+FC8QmZTTkiFmO80eeaiyGGTS0AEW51EPI9
+Nr142PEaQETpjQaQnfNhZGnwTZHTAgMBAAGjUzBRMB0GA1UdDgQWBBQb73HlJHF7
+B4a1PWRkb3aovk7EtDAfBgNVHSMEGDAWgBQb73HlJHF7B4a1PWRkb3aovk7EtDAP
+BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCQJcLh7+NzAukTG4hV
+nVd572U4sCnlXwTN4lnRIkSGbQxOWwbuI7p05iAT9HJrFLm+rV0EOzxyl/fYkPgR
+DKYfj7sGPp4Lo0NyQ3xhoVypqDDmv7Wm9CLy2mVTJ07khW5tV8GHg9mFwgb/XBTQ
+c+RnNalIapqdlkQbxms5HLzKzTVRuDt+wvqcAsiKFwKDAL9DFrp36TP1t023w+vd
+Vb6Ms+l+xFFcxMCxFEOkSPc7szwSce9Q+Hk2C6JdCU30gJPyEBUMl6vD+0l0IFG0
+ZEw1BhBp4d92xHiYgdnDEH1RdLPI0q1r7LJL+5ic+VFWycgbC0KRHCarduYgvMYy
+GbRG
 -----END CERTIFICATE-----
diff -pruN 1:0.10.28-3/t/cert/test2.key 1:0.10.29-1/t/cert/test2.key
--- 1:0.10.28-3/t/cert/test2.key	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/cert/test2.key	2025-12-07 15:22:42.000000000 +0000
@@ -1,15 +1,28 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDy+OVI2u5NBOeB2CyzGnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4
-PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7fJrKFziJxMy4g4Kdn9G659vE7CWu/UAVj
-RUtc+mTBAEfjdbumizmHLG7DmnNhGl3RNGiVNLsUInSMGfUlJRzZJXhI4QIDAQAB
-AoGAEqBB83PVENJvbOTFiHVfUAjGtr3R/Wnwd4jOcjHHZB3fZ9sjVoxJntxfp3s1
-dwZir2rxlqVS6i3VAFiGiVTOGo2Vvzhw2J7f58twCECmnLb2f863AkGEYe4dAndD
-GHGD0WI0CBMD1sT18YCj561o0Wol5deWH0gM9pr2N3HkeIECQQD6hUKFlFhrpaHP
-WNJsl6BxgE6pB5kxLcMcpIQ7P+kHUvtyvCJl5QZJqPrpPGjRsAI5Ph92rpsp/zDp
-/IZNWGVjAkEA+Ele31Rt+XbV32MrLKZgBDBk+Pzss5LTn9fZ5v1k/7hrMk2VVWvk
-AD6n5QiGe/g59woANpPb1T9l956SBf0d6wJABTXOS17pc9uvANP1FGMW6CVl/Wf2
-DKrJ+weE5IKQwyE7r4gwIvRfbBrClSU3fNzvPueG2f4JphbzmnoxBNzIxwJAYivY
-mGNwzHehXx99/byXMHDWK+EN0n8WsBgP75Z3rekEcbJdfpYXY8Via1vwmOnwOW65
-4NqbzHix37PSNw37GwJBALxaGNpREO2Tk+oWOvsD2QyviMVae3mXAJHc6nLVdKDM
-q0YvDT6VdeNYYFTkAuzJacsVXOpn6AnUMFj0OBedMhc=
------END RSA PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQChXBMRP5X9Y0l5
+NogyIZatou1YDT2BDE9tza6dYKUdraJlGo1T4DZf7LoDmKFvIpVqLedppZ9z9Srl
+rsZ8Be6OUbw3kxSyUPDvpauI4XXBR2ZuAHIzTy/Uare11iZSjbT0KATiWviMWCWW
+blyBU5QPMFJJOmcjRDAZocfIUs4CzglRaDorl5qUFJHrmROaIsag/10nnClTwaGw
+j1pHmFitKRHTIrYICYd5wGgstte9mDFz15s9a3f/Dwhc/7neEHYaryRVhR627aDG
+VHHpcMRWr+FC8QmZTTkiFmO80eeaiyGGTS0AEW51EPI9Nr142PEaQETpjQaQnfNh
+ZGnwTZHTAgMBAAECggEAAhemuvqx8A25YQQsgvM9jXXckbTmPxtQ7QcVmb6BlZ9v
+834DJtB0VA0b97pNyZpPXrJGsG+F4E6QZlcRsCpcMWXs72Q4Xfw/tj6AjsSsWWZw
+H8K2Dbqb+lYB7i6J463TvWDGzhqeOM+seRq9l1hlRfHQogN1spbf8CJ8Fo8VqHf3
+OyId5db0nRUFYV9ciPvJSnk8xi+0aJ2LWVAkCaXQkntbI3529x8KPQm9bGUjdMgo
+A+HXAKZTZL7GtGZoSU4jjyymGLM4hPtzXBExbkX7s7HOP1AeWGom4reOC6XNScQq
+ysfs0wVzJAVSl0vFbQfUT6GZ2ZsaTdkT+JgXrYwp5QKBgQDIzY1O/Hj2koVgfKmS
+2+1JVehX8+yjgz3rSNVUauISLoMaYN4hipsKb7Vi6aDFl+Db296Qql7kIzzd/nFk
+UwdBjShUi/xbewGcrYEXbdcXSIxt9ius76eJ9INfa4EmLrZoiX/3Am1u+CcdtcKm
+jkqy1o9zoIPbD6lc242zGFK2nQKBgQDNtukEZop/PngCX0yDGWLxisrvFYSrHxnW
+x9/Fw0B56qmyG2FpFg5WDlErenHXh+KSlQ3pV1sWK/+3pVzgGP2ZK+nv3ThEQJmg
+IbI32dABmUG9+HckxgOuAzfqGW6Y4m5I8YpODjn26V6GeS+xQj0bolr4lZ00QSEG
+R5BxxLLHLwKBgQDAzYzWwFgs+HaUNF5lokFt3syeVplqTsOPPHmI+q9iocJD+6qN
+Lha7qJLTDFvQHYpL0AsdgFhoWOVCieK6X3ZiHHUS6O4/sBXWKEoBAvg5ZPFhS7KX
+8+w768iQQBrbXJLMQOnbdDs7B44XWsQxRAK5QIawkPjJx3norO6bfck9iQKBgA05
+Ze9ffCXD6UkVwUBoQGEQsA0AkB/EBxA0lyEKdTmhKTmG4xMzVMaZcwRdgbX+SUVt
+CZDnibZ6K50tpzPMx3iyRv6hdP2GPZn6sI9AlEuWA5DnyRj3FcN49344FlEDtV1B
+EcgEyBskU2xHnBZEENOW80wpqgpy6WGS9ikqIOgHAoGAeAUetHWIdZZ+Qt6SS1s2
+R17YAiZe88QFAo3uK/dyApLgY7wvbM0n7Cor/3tbpzpzAujUb/qNqTdTXsZIeRfq
+bI767g4/pNl7+jhDUKmDI001YdfHvcRSwjzyjshuSinJY9rydXLUIEWtkTG3f9v8
+d+yLXD2Dz9gF6l2Msj+C3To=
+-----END PRIVATE KEY-----
diff -pruN 1:0.10.28-3/t/cert/test_passphrase.crt 1:0.10.29-1/t/cert/test_passphrase.crt
--- 1:0.10.28-3/t/cert/test_passphrase.crt	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/cert/test_passphrase.crt	2025-12-07 15:22:42.000000000 +0000
@@ -1,17 +1,24 @@
 -----BEGIN CERTIFICATE-----
-MIICozCCAgwCCQDEutRdSs3vZjANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMC
-Q04xEjAQBgNVBAgMCUd1YW5nZG9uZzERMA8GA1UEBwwIU2hlblpoZW4xEjAQBgNV
-BAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0
-LmNvbTEjMCEGCSqGSIb3DQEJARYUZ3VhbmdsaW5sdkBnbWFpbC5jb20wIBcNMTYw
-NDI4MTQ0MzI4WhgPMjE1MTAzMjcxNDQzMjhaMIGUMQswCQYDVQQGEwJDTjESMBAG
-A1UECAwJR3Vhbmdkb25nMREwDwYDVQQHDAhTaGVuWmhlbjESMBAGA1UECgwJT3Bl
-blJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRlc3QuY29tMSMw
-IQYJKoZIhvcNAQkBFhRndWFuZ2xpbmx2QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0B
-AQEFAAOBjQAwgYkCgYEA2KZ+HdH9R2tarxD8PKqu5EYq2BNGlFRg1xJmrw0XZBRM
-UP/VPb+sIeioooz36uhiXfQjExlpBCA/0zNAN+HbFyqpPPTf1qLGrj/dqeE4MJaN
-Bwzxiv3fZnENT65u2qbiFWIY+ATNHgA20d50nxNNjPTzLbkx/nYXL92r4kuAGk0C
-AwEAATANBgkqhkiG9w0BAQUFAAOBgQCfMo0qbcs3kwl1tcNBO5hCcUUJRzyv041V
-ff/nZ/JPIMo/LSZd12K82G/dLRN7uRT9nzqtm+JRkHALHWWWFKi6bdg1vcdOTWqC
-08bCkJHQoXJQQLvvA6gNvnR+0b7L4CrCmrcyYgKDLXVGNP9Wv/PqSWWbxsmqngkA
-Mvy6CVytFw==
+MIIEDTCCAvWgAwIBAgIUZn0PL6eNqoKWughHd0SFO4aE/UswDQYJKoZIhvcNAQEL
+BQAwgZQxCzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ2RvbmcxETAPBgNVBAcM
+CFNoZW5aaGVuMRIwEAYDVQQKDAlPcGVuUmVzdHkxEjAQBgNVBAsMCU9wZW5SZXN0
+eTERMA8GA1UEAwwIdGVzdC5jb20xIzAhBgkqhkiG9w0BCQEWFGd1YW5nbGlubHZA
+Z21haWwuY29tMCAXDTI1MDMwNDAxNDkwNFoYDzIxNTkwNTAxMDE0OTA0WjCBlDEL
+MAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5nZG9uZzERMA8GA1UEBwwIU2hlblpo
+ZW4xEjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYD
+VQQDDAh0ZXN0LmNvbTEjMCEGCSqGSIb3DQEJARYUZ3VhbmdsaW5sdkBnbWFpbC5j
+b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmsvo9t9/lhfavK6S8
+7lqP8lfpV1gZE4ahyRCQI0vgQitlhGjEzvBc7qTtkunQpSWkuHSczOR1WuKzO+PG
+ubH0gjeyvEE2X/cipQ1kjiO9Mv8w2qOm9BkC9Wrob01X4mOhGZUgthfwRs0QFSUh
+tzvjg+aApWyIlpmgE/T8bnCwtPS7BuzwEpTt5a51q8JKOr4AMTy64mrMFQRqtcIk
+GYL9CnoxAjYT6NAcEqoapSLDm1xJ7w2RDL1mqkAdXYUQeNPwMivrtb+rCDD4p99A
+TZ8rOuKQzDSxZ3BdCmR5WUXp9z+NeQE5SBu+vkOYMTvEN+e9ApRRnZTO8UHeRB0+
+6vJZAgMBAAGjUzBRMB0GA1UdDgQWBBSmnXGC+KOqNzyGXoujr3TMe3pepDAfBgNV
+HSMEGDAWgBSmnXGC+KOqNzyGXoujr3TMe3pepDAPBgNVHRMBAf8EBTADAQH/MA0G
+CSqGSIb3DQEBCwUAA4IBAQAokEqnbJNDUICvbsRQBFrDBZXjZaEPu69GrTg9OceB
+N9Qu4CKRTL4WsV6uSbdj+Ox/r21UtCI1590oBpC3RiJ+WnGmpdUGVytGIODt/vJ7
+/R03EBy0o9e7XdlR4GfKMC7FVBsP/kkeacR2fswHYXtQrNlyuLhZW04HTDdYNNqb
+LNCnsmwQ4WR4ek9PdcpeFfMY1Up6hTYZ441K8b6xSzMV0/LjzfmHXWoT3U1rQrfM
+esm9t/R3sOvB/wBWQaRe0BbLYpkS7pcq8NQsWvSUFNl5TzrGtUf8KTyvEEdcUcZf
+im48Ea7WYpE3RYs0laLBk/D3sz7vsK5wfiD0R/ASLE+s
 -----END CERTIFICATE-----
diff -pruN 1:0.10.28-3/t/cert/test_passphrase.key 1:0.10.29-1/t/cert/test_passphrase.key
--- 1:0.10.28-3/t/cert/test_passphrase.key	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/t/cert/test_passphrase.key	2025-12-07 15:22:42.000000000 +0000
@@ -1,18 +1,30 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,679ACC8E69ACAA92
-
-Ssrjp3VU4somCNPiXkWqcudDnvnwbyj/Q0pS07at3lXKbhQSgI1Tzhg9Pm3BXXj5
-mkLdeGG5ocrj1Q9dhtmZgZeHHQIiynZBhjBu1Y+HPef8jXOWLrCOi8EKiWkJ2qG3
-V1KFM/95CcDt0mRLykUXEL3IpUst05SFb9XwiLokB7ypeu3NhgNUHjL6G+ubB4ri
-TOUjCW4pEoNHjdC22IiqSncwCVhluYSGhr6ktHKehZMhYIXmL1wmSLdhTlsPXCQl
-xvYILQ2vJcKIR1BkeYYPD/OQC6zCZlXIErzfgeZiz2+NTudKYpb9VmsQKsO+R8L7
-tZ/fNaR0vk8bbimMHgStAV4acVsC/7WxsqOjMJ8VTq1iqhYPl6N7kRdR3H3kSSOm
-cN9T3SrOHDVaHbnWgToaOE4mKFjvFSLIOcWgus0iOHWXmY+SLG+Ndag3oVB6R9oB
-cAHX19mq99+GhzA8IV4I0En2UCKQhnGPvkM+9mcCDxhRETlwncDjlMGOHpQ65J9r
-eReVPIpnDkvHxPGTtsR3ZHTdWTZb+C0W2N3QIlJKrOzxFmfoj++yG3tMX42aDY0g
-DVkrXgcKobiWN0AVrJNAwfG7uObKSCFYgz/0RRMCO4cjXRW99nxdjVDZhyc6R0Te
-jzuF04okkOLNb25n2hP+yIULrn+6Nv/uHtFI0j0n3hOzcKh//dNbACSAKgkHni9g
-JKDFJXgLJxf+Wc3So0DF9gYMKJJ+WbcdVT9gkC7RyQHlC90Pn7kNXzHr0ZawUsNI
-ZxSkL4dMhYAfA4lUBJbOkwbSurv97LinOSRffpM0Nmf7VNw/Ue15eg==
------END RSA PRIVATE KEY-----
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQ2v8WxwirSc2M7jaZ
+mrWsPgICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEAlDrk7LLHI/jJqN
+QJ6z8MYEggTQtV86xHehGFn/Qkn5qSZicJ/sDLXQ+m4dFXgw9KMnm0zLG8kzEmb7
+sobUJ4CnwmpBv/GmpPxgy5v1fuvc8HXRJWr7xwv1FoD4lwVOWVFv/qTaII4TfcC6
+3vKqO7MbRaksyI/NmmepGKTGhbzW6XlEdE1TCG7tO02I3puSN0MbJACTpop53TG9
+g+b8HXegPXkLJ2p+ECjUqPwA+h6jZi2pIAztMHQpIizokomrLJ4+2ol3czJpA5nn
+n8JbuYt1XWekq+PBi4l0+lAbk/RiLLK1PCN/Njp7iBBAtNtz9+yJd2Ev7wNDDxKk
+dW73s8uTGde/n6hR4sl/akK8HLaw9tDcWsaHbIMl8zn3WZXksVUfb6CZVOU+BjBa
+yZu3CnP1JTda16HgdZ5dGSNPNbrUeLLIRzM/OxK0yTtmkexOVIgSW0hGE1BXxFXk
+v+mnzrjFrHNRQFhoCFciPqv+WfXsecSASeN0aLWP/RP6CLz6QXWQaK68Dn92v/WP
+xc1NKk8l54wNCLqGaSXadUoTqEdHQH3GV5EkXP7MTb+fYi9/Y/S8wcXJtjUi3ABe
+W+XCOh4zuuQPG7C3i6l/jLIL1ogQNYldad7MoM8eBU8u+TF+NqiJjTLCB1Quycqn
+sN5oVS1VZfsx8XCFsvUAZgxzhjeXViBfwxKSKyT0b1drQZaY58LnyzjmzGkOpRfO
+uzGzWPvHRxkGb9071XaRxD8YDLIngHCw3F2USto88efzmvBfB8ZIxk90S9TZZDeJ
+rqIKpNXsfM4nCR07DW48n3IoiHLCtRFinfpboS5/47ZN4XBE7Q/QNJrWa2P07aWR
+HdOBdD/N3DnM5bbSz7m+5TA+xC4/hEWLXt7Au+vnnhVyDdU80a3+3JY7y4vScHLS
+Fn2grTk5F9pqieNhwawpXUCYNGFtJOY8LOQpUZdMpy6n6N5qzSjZ+vgE1Nwr0G/S
++n8ORUcJzF3s8h+u+SN8OC9W6chnftSIWKiqGsU4oM0FFkr2nOrfNNKqAQo2OU3W
++5lJovyqAcxtj+4qQsBrYzH7vRzdkigXhb/R7tRDnBRWBpWlMmnUhkaWG8EI0JLs
+gjKNOUFCT1HgTSfonRxScDE5IAg3JxieNz9ppDCphvc0ii3wwdS7uwstKGZCC+Wr
+lQsiV0Wa4WlWVMfLCONI13+zeOli4BmwepgkjJc+YogvLodblcHN14kTTCDvhDMp
+KZglwFT+3zkxH9ArnO7/a8WQCWZV6bJ29HFUfAioE8Sie04WD4zgYEfU02r9A+vq
+wl/w5UK6eXOO6Hz2OrfYh2dZXiCOLp+DhftKq6MKIIAcqFuAzrPvzxd7Uup/fxyX
+n2Ub4YKmUV89RzJ2ipZRjiBcQo+9h4O9dIh4+2I0m7NPALqirutCFBe2cHu/Fwho
+SzacLI21OzWLKbyZ2D6ueH4NrhnOZ10bo/lKbc5d3JzuGv6KW1NmGo32qiKeKNzG
+Q52+sm0/TLiLxy2RlqJ1p7R9NLLZTY8bZTD125n2RtNaJWT564vseY8SaiUW62r8
+ksU5aPINa7Qkf4xgsmkSJymFs/9AjXfSCS/aZ13PQMUuI5A4fY9IZjSoiUlSx1bX
++2B91azW4yfHcAJrluIjZ13LvBU08ooPKPm2TazDi89/8GjBMpHUQN4=
+-----END ENCRYPTED PRIVATE KEY-----
diff -pruN 1:0.10.28-3/util/build-with-dd.sh 1:0.10.29-1/util/build-with-dd.sh
--- 1:0.10.28-3/util/build-with-dd.sh	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/util/build-with-dd.sh	2025-12-07 15:22:42.000000000 +0000
@@ -12,35 +12,18 @@ force=$2
 
 add_fake_shm_module="--add-module=$root/t/data/fake-shm-module"
 
-add_http3_module=--with-http_v3_module
-answer=`$root/util/ver-ge "$NGINX_VERSION" 1.25.1`
-if [ "$OPENSSL_VER" = "1.1.0l" ] || [ "$answer" = "N" ]; then
-    add_http3_module=""
-fi
-
-disable_pcre2=--without-pcre2
-answer=`$root/util/ver-ge "$NGINX_VERSION" 1.25.1`
-if [ "$answer" = "N" ] || [ "$USE_PCRE2" = "Y" ]; then
-    disable_pcre2=""
-fi
-if [ "$USE_PCRE2" = "Y" ]; then
-    PCRE_INC=$PCRE2_INC
-    PCRE_LIB=$PCRE2_LIB
-fi
-
 time ngx-build $force $version \
             --with-threads \
             --with-pcre-jit \
-            $disable_pcre2 \
             --with-ipv6 \
-            --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE_INC -I$OPENSSL_INC -DDDEBUG=1" \
+            --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE2_INC -I$OPENSSL_INC -DDDEBUG=1" \
             --with-http_v2_module \
-            $add_http3_module \
+            --with-http_v3_module \
             --with-http_realip_module \
             --with-http_ssl_module \
             --add-module=$root/../ndk-nginx-module \
             --add-module=$root/../set-misc-nginx-module \
-            --with-ld-opt="-L$PCRE_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB" \
+            --with-ld-opt="-L$PCRE2_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB" \
             --without-mail_pop3_module \
             --without-mail_imap_module \
             --with-http_image_filter_module \
diff -pruN 1:0.10.28-3/util/build-without-ssl.sh 1:0.10.29-1/util/build-without-ssl.sh
--- 1:0.10.28-3/util/build-without-ssl.sh	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/util/build-without-ssl.sh	2025-12-07 15:22:42.000000000 +0000
@@ -10,41 +10,19 @@ version=${1:-1.4.1}
 home=~
 force=$2
 
-# the ngx-build script is from https://github.com/agentzh/nginx-devel-utils
-
-            #--add-module=$home/work/nginx_upload_module-2.2.0 \
-
-            #--without-pcre \
-            #--without-http_rewrite_module \
-            #--without-http_autoindex_module \
-            #--with-cc=gcc46 \
-            #--with-cc=clang \
-            #--without-http_referer_module \
-            #--with-http_spdy_module \
-
 add_fake_shm_module="--add-module=$root/t/data/fake-shm-module"
 
-disable_pcre2=--without-pcre2
-answer=`$root/util/ver-ge "$NGINX_VERSION" 1.25.1`
-if [ "$answer" = "N" ] || [ -n "$PCRE2_VER" ]; then
-    disable_pcre2=""
-fi
-if [ -n "$PCRE2_VER" ]; then
-    PCRE_INC=$PCRE2_INC
-    PCRE_LIB=$PCRE2_LIB
-fi
-
+rm -fr buildroot
 time ngx-build $force $version \
             --with-threads \
             --with-pcre-jit \
-            $disable_pcre2 \
             --with-ipv6 \
-            --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE_INC" \
+            --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE2_INC" \
             --with-http_v2_module \
             --with-http_realip_module \
             --add-module=$root/../ndk-nginx-module \
             --add-module=$root/../set-misc-nginx-module \
-            --with-ld-opt="-L$PCRE_LIB -Wl,-rpath,$PCRE_LIB:$LIBDRIZZLE_LIB" \
+            --with-ld-opt="-L$PCRE2_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB" \
             --without-mail_pop3_module \
             --without-mail_imap_module \
             --with-http_image_filter_module \
diff -pruN 1:0.10.28-3/util/build.sh 1:0.10.29-1/util/build.sh
--- 1:0.10.28-3/util/build.sh	2025-01-17 05:24:35.000000000 +0000
+++ 1:0.10.29-1/util/build.sh	2025-12-07 15:22:42.000000000 +0000
@@ -24,64 +24,49 @@ force=$2
 
 add_fake_shm_module="--add-module=$root/t/data/fake-shm-module"
 
-add_http3_module=--with-http_v3_module
 answer=`$root/util/ver-ge "$version" 1.25.1`
-if [ "$OPENSSL_VER" = "1.1.0l" ] || [ "$answer" = "N" ]; then
-    add_http3_module=""
-fi
-
-disable_pcre2=--without-pcre2
-answer=`$root/util/ver-ge "$version" 1.25.1`
-if [ "$answer" = "N" ] || [ "$USE_PCRE2" = "Y" ]; then
-    disable_pcre2=""
-fi
-if [ "$USE_PCRE2" = "Y" ]; then
-    PCRE_INC=$PCRE2_INC
-    PCRE_LIB=$PCRE2_LIB
-fi
 
 time ngx-build $force $version \
-            --with-threads \
-            --with-pcre-jit \
-            $disable_pcre2 \
-            --with-ipv6 \
-            --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE_INC -I$OPENSSL_INC" \
-            --with-http_v2_module \
-            $add_http3_module \
-            --with-http_realip_module \
-            --with-http_ssl_module \
-            --add-module=$root/../ndk-nginx-module \
-            --add-module=$root/../set-misc-nginx-module \
-            --with-ld-opt="-L$PCRE_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB" \
-            --without-mail_pop3_module \
-            --without-mail_imap_module \
-            --with-http_image_filter_module \
-            --without-mail_smtp_module \
-            --with-stream \
-            --with-stream_ssl_module \
-            --without-http_upstream_ip_hash_module \
-            --without-http_memcached_module \
-            --without-http_auth_basic_module \
-            --without-http_userid_module \
-            --with-http_auth_request_module \
-                --add-module=$root/../echo-nginx-module \
-                --add-module=$root/../memc-nginx-module \
-                --add-module=$root/../srcache-nginx-module \
-                --add-module=$root \
-                --add-module=$root/../lua-upstream-nginx-module \
-              --add-module=$root/../headers-more-nginx-module \
-                --add-module=$root/../drizzle-nginx-module \
-                --add-module=$root/../rds-json-nginx-module \
-                --add-module=$root/../coolkit-nginx-module \
-                --add-module=$root/../redis2-nginx-module \
-                --add-module=$root/../stream-lua-nginx-module \
-                --add-module=$root/t/data/fake-module \
-                $add_fake_shm_module \
-                --add-module=$root/t/data/fake-delayed-load-module \
-                --with-http_gunzip_module \
-                --with-http_dav_module \
+          --with-threads \
+          --with-pcre-jit \
+          --with-ipv6 \
+          --with-cc-opt="-DNGX_LUA_USE_ASSERT -I$PCRE2_INC -I$OPENSSL_INC" \
+          --with-http_v2_module \
+          --with-http_v3_module \
+          --with-http_realip_module \
+          --with-http_ssl_module \
+          --add-module=$root/../ndk-nginx-module \
+          --add-module=$root/../set-misc-nginx-module \
+          --with-ld-opt="-L$PCRE2_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE2_LIB:$LIBDRIZZLE_LIB:$OPENSSL_LIB" \
+          --without-mail_pop3_module \
+          --without-mail_imap_module \
+          --with-http_image_filter_module \
+          --without-mail_smtp_module \
+          --with-stream \
+          --with-stream_ssl_module \
+          --without-http_upstream_ip_hash_module \
+          --without-http_memcached_module \
+          --without-http_auth_basic_module \
+          --without-http_userid_module \
+          --with-http_auth_request_module \
+          --add-module=$root/../echo-nginx-module \
+          --add-module=$root/../memc-nginx-module \
+          --add-module=$root/../srcache-nginx-module \
+          --add-module=$root \
+          --add-module=$root/../lua-upstream-nginx-module \
+          --add-module=$root/../headers-more-nginx-module \
+          --add-module=$root/../drizzle-nginx-module \
+          --add-module=$root/../rds-json-nginx-module \
+          --add-module=$root/../coolkit-nginx-module \
+          --add-module=$root/../redis2-nginx-module \
+          --add-module=$root/../stream-lua-nginx-module \
+          --add-module=$root/t/data/fake-module \
+          $add_fake_shm_module \
+          --add-module=$root/t/data/fake-delayed-load-module \
+          --with-http_gunzip_module \
+          --with-http_dav_module \
           --with-select_module \
           --with-poll_module \
-                $opts \
-                --with-debug
+          $opts \
+          --with-debug
 
diff -pruN 1:0.10.28-3/util/run-ci.sh 1:0.10.29-1/util/run-ci.sh
--- 1:0.10.28-3/util/run-ci.sh	1970-01-01 00:00:00.000000000 +0000
+++ 1:0.10.29-1/util/run-ci.sh	2025-12-07 15:22:42.000000000 +0000
@@ -0,0 +1,193 @@
+#!/bin/bash
+
+#export CC=clang
+export CC=gcc
+export NGX_BUILD_CC=$CC
+
+mkdir -p download-cache
+
+export JOBS=$(nproc)
+export NGX_BUILD_JOBS=$JOBS
+export VALGRIND_INC=/usr/include/valgrind/
+
+export LUAJIT_PREFIX=/opt/luajit21
+export LUAJIT_LIB=$LUAJIT_PREFIX/lib
+export LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1
+export LUA_INCLUDE_DIR=$LUAJIT_INC
+
+export PCRE2_VER=10.45
+export PCRE2_PREFIX=/opt/pcre2
+export PCRE2_LIB=$PCRE2_PREFIX/lib
+export PCRE2_INC=$PCRE2_PREFIX/include
+
+export OPENSSL_VER=3.5.0
+export OPENSSL_PATCH_VER=3.5.0
+export OPENSSL_PREFIX=/opt/ssl3
+export OPENSSL_LIB=$OPENSSL_PREFIX/lib
+export OPENSSL_INC=$OPENSSL_PREFIX/include
+
+export LIBDRIZZLE_PREFIX=/opt/drizzle
+export LIBDRIZZLE_INC=$LIBDRIZZLE_PREFIX/include/libdrizzle-1.0
+export LIBDRIZZLE_LIB=$LIBDRIZZLE_PREFIX/lib
+export DRIZZLE_VER=2011.07.21
+
+#export TEST_NGINX_SLEEP=0.006
+export NGINX_VERSION=1.27.1
+#export NGX_BUILD_ASAN=1
+
+export PATH=/opt/bin:$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH
+
+if [ ! -f /opt/bin/curl ]; then
+    wget https://github.com/stunnel/static-curl/releases/download/8.14.1/curl-linux-x86_64-glibc-8.14.1.tar.xz
+    tar -xf curl-linux-x86_64-glibc-8.14.1.tar.xz
+    tar -xf curl-linux-x86_64-glibc-8.14.1.tar.xz
+    sudo mkdir -p /opt/bin
+    sudo mv curl /opt/bin/
+fi
+
+function git_download()
+{
+    dir=${!#}
+    if [ ! -d $dir ]; then
+        git clone $@
+    fi
+}
+
+function download_deps()
+{
+    if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then
+        wget -P download-cache https://github.com/openresty/openresty-deps-prebuild/releases/download/v20230902/drizzle7-$DRIZZLE_VER.tar.gz
+    fi
+
+    if [ ! -f download-cache/pcre2-$PCRE2_VER.tar.gz ]; then
+       wget -P download-cache https://github.com/PCRE2Project/pcre2/releases/download/pcre2-${PCRE2_VER}/pcre2-${PCRE2_VER}.tar.gz
+    fi
+
+    if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then
+       wget -P download-cache https://github.com/openssl/openssl/releases/download/openssl-$OPENSSL_VER/openssl-$OPENSSL_VER.tar.gz
+    fi
+
+    git_download https://github.com/openresty/test-nginx.git
+    git_download https://github.com/openresty/openresty.git ../openresty
+    git_download https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx
+    git_download https://github.com/openresty/openresty-devel-utils.git
+    git_download https://github.com/openresty/mockeagain.git
+    git_download https://github.com/openresty/lua-cjson.git lua-cjson
+    git_download https://github.com/openresty/lua-upstream-nginx-module.git ../lua-upstream-nginx-module
+    git_download https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
+    git_download https://github.com/openresty/nginx-eval-module.git ../nginx-eval-module
+    git_download https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module
+    git_download https://github.com/FRiCKLE/ngx_coolkit.git ../coolkit-nginx-module
+    git_download https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module
+    git_download https://github.com/openresty/drizzle-nginx-module.git ../drizzle-nginx-module
+    git_download https://github.com/openresty/set-misc-nginx-module.git ../set-misc-nginx-module
+    git_download https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module
+    git_download https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module
+    git_download https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module
+    git_download https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module
+    git_download https://github.com/openresty/lua-resty-core.git ../lua-resty-core
+    git_download https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
+    git_download https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql
+    git_download https://github.com/openresty/lua-resty-string.git ../lua-resty-string
+    git_download https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module
+    git_download -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2
+}
+
+function make_deps()
+{
+    if [ "$TEST_NGINX_BUILD_DEPS" = "n" ]; then
+        return
+    fi
+
+    cd luajit2/
+    make clean
+    make -j"$JOBS" CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS="-DLUAJIT_USE_VALGRIND -I$VALGRIND_INC -DLUAJIT_USE_SYSMALLOC -DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2" > build.log 2>&1 || (cat build.log && exit 1)
+    sudo make install PREFIX=$LUAJIT_PREFIX
+    cd ..
+ 
+    tar zxf download-cache/openssl-$OPENSSL_VER.tar.gz
+    cd openssl-$OPENSSL_VER/
+    patch -p1 < ../../openresty/patches/openssl-$OPENSSL_PATCH_VER-sess_set_get_cb_yield.patch > ssl.log
+    ./config -DOPENSSL_TLS_SECURITY_LEVEL=1 shared enable-ssl3 enable-ssl3-method -g -O2 --prefix=$OPENSSL_PREFIX --libdir=lib -DPURIFY >> ssl.log
+    make -j$JOBS >> ssl.log
+    sudo make PATH=$PATH install_sw >> ssl.log
+    cd ..
+ 
+    tar zxf download-cache/pcre2-$PCRE2_VER.tar.gz;
+    cd pcre2-$PCRE2_VER/;
+    ./configure --prefix=$PCRE2_PREFIX --enable-jit --enable-utf
+    sudo PATH=$PATH make install
+    cd ..
+ 
+    tar xzf download-cache/drizzle7-$DRIZZLE_VER.tar.gz && cd drizzle7-$DRIZZLE_VER
+    ./configure --prefix=$LIBDRIZZLE_PREFIX --without-server
+    make libdrizzle-1.0 -j$JOBS
+    sudo make install-libdrizzle-1.0
+    cd ..
+
+    cd mockeagain/ && make CC=$CC -j$JOBS
+    cd ..
+
+    cd lua-cjson/ && make -j$JOBS && sudo make install
+    cd ..
+}
+
+function make_ngx()
+{
+    if [ "$TEST_NGINX_FRESH_BUILD" = "y" ]; then
+        rm -fr buildroot
+    fi
+
+    sh util/build.sh $NGINX_VERSION 2>&1 | tee build.log
+}
+
+download_deps
+make_deps
+make_ngx
+
+find t -name "*.t" | xargs reindex >/dev/null 2>&1
+
+#nginx -V
+
+export LD_PRELOAD=$PWD/mockeagain/mockeagain.so
+export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH
+
+#export ASAN_OPTIONS=detect_leaks=0,log_path=/tmp/asan/asan,log_exe_name=true
+#export LD_PRELOAD=/lib64/libasan.so.5:$PWD/mockeagain/mockeagain.so
+
+export LD_LIBRARY_PATH=$LUAJIT_LIB:$OPENSSL_LIB:$LD_LIBRARY_PATH
+export TEST_NGINX_RESOLVER=8.8.4.4
+
+#export TEST_NGINX_NO_CLEAN=1
+#export TEST_NGINX_CHECK_LEAK=1
+#export TEST_NGINX_CHECK_LEAK_COUNT=100
+#export TEST_NGINX_TIMEOUT=5
+
+#export TEST_NGINX_USE_VALGRIND=1
+#export TEST_NGINX_VALGRIND_EXIT_ON_FIRST_ERR=1
+#export TEST_NGINX_USE_HTTP2=1
+
+export MALLOC_PERTURB_=33
+export TEST_NGINX_HTTP3_CRT=$PWD/t/cert/http3/http3.crt
+export TEST_NGINX_HTTP3_KEY=$PWD/t/cert/http3/http3.key
+#export TEST_NGINX_USE_HTTP3=1
+
+#export TEST_NGINX_VERBOSE=1
+
+#export TEST_NGINX_EVENT_TYPE=poll
+#export TEST_NGINX_POSTPONE_OUTPUT=1
+#export MOCKEAGAIN=r
+#export MOCKEAGAIN=w
+#export MOCKEAGAIN=rw
+#export MOCKEAGAIN_VERBOSE=1
+
+ldd `which nginx`|grep -E 'luajit|ssl|pcre'
+which nginx
+nginx -V
+
+#export TEST_NGINX_INIT_BY_LUA="debug.sethook(function () collectgarbage() end, 'l') jit.off() package.path = '/usr/share/lua/5.1/?.lua;$PWD/../lua-resty-core/lib/?.lua;$PWD/../lua-resty-lrucache/lib/?.lua;' .. (package.path or '') require 'resty.core' require('resty.core.base').set_string_buf_size(1) require('resty.core.regex').set_buf_grow_ratio(1)"
+
+
+# comment out TEST_NGINX_RANDOMIZE when debugging one test case
+export TEST_NGINX_RANDOMIZE=1
+prove -j$JOBS -I. -Itest-nginx/inc -Itest-nginx/lib -r t/
