From ff50e05852408aba0736e1fb9532824589df177f Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Thu, 23 Oct 2025 12:27:07 +0800 Subject: [PATCH 1/7] feature: tcpsock sslhandshake alpn --- src/ngx_http_lua_socket_tcp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 85cf49069e..45d722e4a1 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -1676,7 +1676,7 @@ ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r, ngx_http_lua_socket_tcp_upstream_t *u, ngx_ssl_session_t *sess, int enable_session_reuse, ngx_str_t *server_name, int verify, int ocsp_status_req, STACK_OF(X509) *chain, EVP_PKEY *pkey, - const char **errmsg) + ngx_str_t *alpn, const char **errmsg) { ngx_int_t rc, i; ngx_connection_t *c; @@ -1843,6 +1843,24 @@ ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r, #endif } +#if (defined TLSEXT_TYPE_application_layer_protocol_negotiation) + if (alpn->len > 255) { + *errmsg = "too large ALPN list"; + return NGX_ERROR; + } + + if (SSL_set_alpn_protos(c->ssl->connection, + alpn->data, + (unsigned int) (alpn->len)) != 0) + { + *errmsg = "SSL_set_alpn_protos failed"; + return NGX_ERROR; + } +#else + *errmsg = "no ALPN support"; + return NGX_ERROR; +#endif + if (server_name == NULL || server_name->len == 0) { u->ssl_name.len = 0; From 937ef89d3a545d55d779b0d744d87a0019820e82 Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Thu, 23 Oct 2025 12:52:32 +0800 Subject: [PATCH 2/7] test: tcpsock sslhandshake with alpn --- t/129-ssl-socket.t | 138 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/t/129-ssl-socket.t b/t/129-ssl-socket.t index 5651f3f817..1ec2c3aed0 100644 --- a/t/129-ssl-socket.t +++ b/t/129-ssl-socket.t @@ -3046,3 +3046,141 @@ SSL reused session [error] [alert] --- timeout: 10 + + + +=== TEST 36: sslhandshake (ALPN, h2) +--- 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; + + http2 on; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- config + server_tokens off; + lua_ssl_protocols TLSv1.3; + + 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", nil, nil, {"h2"}) + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + } +--- request +GET /t +--- response_body_like +connected: 1 +ssl handshake: cdata +close: 1 nil + +--- log_level: debug +--- error_log +SSL ALPN supported by client: h2 +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 10 + + + +=== TEST 37: sslhandshake (ALPN, h2, http/1.1, my-proto) +--- 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; + + http2 on; + + location / { + content_by_lua_block { + ngx.exit(200) + } + } + } +--- config + server_tokens off; + lua_ssl_protocols TLSv1.3; + + 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", nil, nil, {"h2", "http/1.1", "my-proto"}) + if not session then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(session)) + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + collectgarbage() + } + } +--- request +GET /t +--- response_body_like +connected: 1 +ssl handshake: cdata +close: 1 nil + +--- log_level: debug +--- error_log +SSL ALPN supported by client: h2 +SSL ALPN supported by client: http/1.1 +SSL ALPN supported by client: my-proto +--- no_error_log +SSL reused session +[error] +[alert] +--- timeout: 10 From 32cbfccaaf629e56e1a791354c42890041a775f6 Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Thu, 23 Oct 2025 13:31:22 +0800 Subject: [PATCH 3/7] fix: handle null alpn --- src/ngx_http_lua_socket_tcp.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 45d722e4a1..d897eeec9a 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -1843,23 +1843,28 @@ ngx_http_lua_ffi_socket_tcp_sslhandshake(ngx_http_request_t *r, #endif } + if (alpn != NULL && alpn->data != NULL) { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "lua ssl ALPN: \"%V\"", alpn); + #if (defined TLSEXT_TYPE_application_layer_protocol_negotiation) - if (alpn->len > 255) { - *errmsg = "too large ALPN list"; - return NGX_ERROR; - } + if (alpn->len > 255) { + *errmsg = "too large ALPN list"; + return NGX_ERROR; + } - if (SSL_set_alpn_protos(c->ssl->connection, - alpn->data, - (unsigned int) (alpn->len)) != 0) - { - *errmsg = "SSL_set_alpn_protos failed"; - return NGX_ERROR; - } + if (SSL_set_alpn_protos(c->ssl->connection, + alpn->data, + (unsigned int) (alpn->len)) != 0) + { + *errmsg = "SSL_set_alpn_protos failed"; + return NGX_ERROR; + } #else - *errmsg = "no ALPN support"; - return NGX_ERROR; + *errmsg = "no ALPN support"; + return NGX_ERROR; #endif + } if (server_name == NULL || server_name->len == 0) { u->ssl_name.len = 0; From f30da149bd563d92d22aaac1b613582606a56d51 Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Thu, 23 Oct 2025 13:36:01 +0800 Subject: [PATCH 4/7] docs: add alpn --- README.markdown | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 8c6edec844..9545e365fa 100644 --- a/README.markdown +++ b/README.markdown @@ -8191,7 +8191,7 @@ This method was first introduced in the `v0.10.22` release. tcpsock:sslhandshake -------------------- -**syntax:** *session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?)* +**syntax:** *session, err = tcpsock:sslhandshake(reused_session?, server_name?, ssl_verify?, send_status_req?, alpn?)* **context:** *rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_client_hello_by_lua** @@ -8227,6 +8227,10 @@ to validate the server name in the server certificate. The optional `send_status_req` argument takes a boolean that controls whether to send the OCSP status request in the SSL handshake request (which is for requesting OCSP stapling). +The optional `alpn` argument specifies the Application-Layer Protocol Negotiation (ALPN) +extension value to be sent during the SSL handshake. It accepts a Lua table where each +element is a string representing a protocol name. For example: `{ "h2", "http/1.1" }`. + For connections that have already done SSL/TLS handshake, this method returns immediately. From bbf96166ac2b2227253a88b8c965cf8103536beb Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Fri, 24 Oct 2025 17:33:23 +0800 Subject: [PATCH 5/7] chore: use patched lua-resty-core --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4aebcf6996..98c2def936 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,7 +106,7 @@ install: - git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module - git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module - git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module - - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core + - git clone https://github.com/bzp2010/lua-resty-core.git -b "bzp/feat-tcpsock-sslhandshake-alpn" ../lua-resty-core - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql - git clone https://github.com/spacewander/lua-resty-rsa.git ../lua-resty-rsa From af0d77b555aded646a21cce0e1d0c9298ed85b3e Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Fri, 24 Oct 2025 23:03:58 +0800 Subject: [PATCH 6/7] tmp: trigger ci --- tmp | 1 + 1 file changed, 1 insertion(+) create mode 100644 tmp diff --git a/tmp b/tmp new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tmp @@ -0,0 +1 @@ +1 From ab47425a02243d490c2a09900d0a3a3705cfc44c Mon Sep 17 00:00:00 2001 From: bzp2010 Date: Fri, 24 Oct 2025 23:37:45 +0800 Subject: [PATCH 7/7] cleanup --- tmp | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tmp diff --git a/tmp b/tmp deleted file mode 100644 index d00491fd7e..0000000000 --- a/tmp +++ /dev/null @@ -1 +0,0 @@ -1